initial upload
This commit is contained in:
parent
a0aa9cc28e
commit
f857f43df4
553 changed files with 46169 additions and 13 deletions
12
src/Web/Insight.Web/.config/dotnet-tools.json
Normal file
12
src/Web/Insight.Web/.config/dotnet-tools.json
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"dotnet-ef": {
|
||||
"version": "7.0.5",
|
||||
"commands": [
|
||||
"dotnet-ef"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
14
src/Web/Insight.Web/App.razor
Normal file
14
src/Web/Insight.Web/App.razor
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<CascadingAuthenticationState>
|
||||
<Router AppAssembly="@typeof(App).Assembly">
|
||||
<Found Context="routeData">
|
||||
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
|
||||
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
|
||||
</Found>
|
||||
<NotFound>
|
||||
<PageTitle>Not found</PageTitle>
|
||||
<LayoutView Layout="@typeof(MainLayout)">
|
||||
<p role="alert">404</p>
|
||||
</LayoutView>
|
||||
</NotFound>
|
||||
</Router>
|
||||
</CascadingAuthenticationState>
|
||||
47
src/Web/Insight.Web/Components/Cards/InfoCard.razor
Normal file
47
src/Web/Insight.Web/Components/Cards/InfoCard.razor
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<MudLink Href="@Href" OnClick="OnClick" Underline="Underline.None">
|
||||
<MudPaper Elevation="25">
|
||||
<MudCard Elevation="0">
|
||||
<MudCardHeader>
|
||||
<CardHeaderAvatar>
|
||||
@if (Icon is not null)
|
||||
{
|
||||
<MudIcon Icon="@Icon" Color="@(IconColor ?? Color.Inherit)" Style="width:4vh; height:4vh;" />
|
||||
}
|
||||
</CardHeaderAvatar>
|
||||
<CardHeaderContent>
|
||||
<MudText Typo="Typo.subtitle2" Class="mud-text-secondary" Color="@(KeyColor ?? Color.Inherit)" Style="font-size: 0.96rem;">
|
||||
@Key
|
||||
</MudText>
|
||||
<MudTextField T="string" Variant="Variant.Text" DisableUnderLine ReadOnly
|
||||
Text="@Text" Lines="@Lines" Class="mt-n1" Style="font-size: 0.99rem; margin: 0;" />
|
||||
</CardHeaderContent>
|
||||
</MudCardHeader>
|
||||
</MudCard>
|
||||
</MudPaper>
|
||||
</MudLink>
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
public string? Href { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback OnClick { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? Icon { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Color? IconColor { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? Key { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Color? KeyColor { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? Text { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public int Lines { get; set; } = 1;
|
||||
}
|
||||
57
src/Web/Insight.Web/Components/Cards/KeyValueCard.razor
Normal file
57
src/Web/Insight.Web/Components/Cards/KeyValueCard.razor
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
@typeparam T
|
||||
|
||||
<MudLink Href="@Href" OnClick="OnClick" Underline="Underline.None">
|
||||
<MudPaper Elevation="25">
|
||||
<MudCard Elevation="0">
|
||||
<MudCardHeader>
|
||||
<CardHeaderAvatar>
|
||||
@if (Icon is not null)
|
||||
{
|
||||
<MudIcon Icon="@Icon" Color="@(IconColor ?? Color.Inherit)" Style="font-size: 2.50rem;" />
|
||||
}
|
||||
</CardHeaderAvatar>
|
||||
<CardHeaderContent>
|
||||
<MudText Typo="Typo.subtitle2" Class="mud-text-secondary" Color="@(KeyColor ?? Color.Inherit)" Style="font-size: 0.96rem;">
|
||||
@Key
|
||||
</MudText>
|
||||
<MudText Color="@(ValueColor ?? Color.Primary)" Style="font-size: 0.99rem;">
|
||||
@if (Value is null)
|
||||
{
|
||||
@("-")
|
||||
}
|
||||
else
|
||||
{
|
||||
@Value
|
||||
}
|
||||
</MudText>
|
||||
</CardHeaderContent>
|
||||
</MudCardHeader>
|
||||
</MudCard>
|
||||
</MudPaper>
|
||||
</MudLink>
|
||||
|
||||
@code{
|
||||
[Parameter]
|
||||
public string? Href { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback OnClick { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? Icon { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Color? IconColor { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string? Key { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Color? KeyColor { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public T? Value { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Color? ValueColor { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
<PageTitle>@Title</PageTitle>
|
||||
|
||||
@*@if (Loading)
|
||||
{
|
||||
<MudProgressLinear Color="Color.Primary" Indeterminate="true" />
|
||||
|
||||
<div class="center-screen" style="height: calc(100vh - 70px);">
|
||||
<MudProgressCircular Color="Color.Primary" Size="Size.Large" Indeterminate="true" />
|
||||
</div>
|
||||
|
||||
return;
|
||||
}*@
|
||||
|
||||
<div class="mt-7 mb-7 ml-4">
|
||||
<div class="d-flex mb-3">
|
||||
<div>
|
||||
@* <MudStack Row="true">
|
||||
<MudBreadcrumbs Items="Breadcrumbs" Separator="" Style="padding: 0px;">
|
||||
<ItemTemplate Context="item">
|
||||
<MudChip Href="@(item.Href)" Disabled="@(item.Disabled)" Size="Size.Medium" Variant="Variant.Outlined" SelectedColor="Color.Inherit" Color="Color.Transparent">
|
||||
@item.Text
|
||||
</MudChip>
|
||||
</ItemTemplate>
|
||||
</MudBreadcrumbs>
|
||||
</MudStack>*@
|
||||
<MudStack Row="true">
|
||||
<MudBreadcrumbs Items="Breadcrumbs" Style="padding: 0px;" />
|
||||
</MudStack>
|
||||
</div>
|
||||
@if (LoadData is not null && DisableReload is false)
|
||||
{
|
||||
<div class="ml-auto">
|
||||
@if (Loading)
|
||||
{
|
||||
<MudProgressCircular Color="Color.Primary" Size="Size.Small" Indeterminate="true" Style="padding: 0px; display: inherit;" />
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudIconButton OnClick="OnRefreshAsync" Icon="@Icons.Material.Filled.Refresh" Size="Size.Medium" Color="Color.Inherit" Style="padding: 0px;" />
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-7">
|
||||
@if (Content is not null)
|
||||
{
|
||||
@Content
|
||||
}
|
||||
</div>
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
using Insight.Web.Constants;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MudBlazor;
|
||||
|
||||
namespace Insight.Web.Components.Containers;
|
||||
|
||||
public partial class BaseContainer
|
||||
{
|
||||
[Parameter]
|
||||
public string Title { get; set; } = Global.Name;
|
||||
|
||||
[Parameter]
|
||||
public List<BreadcrumbItem>? Breadcrumbs { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment? BreadcrumbAction { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment? Content { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<Task>? LoadData { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool DisableReload { get; set; }
|
||||
|
||||
|
||||
private bool _loading;
|
||||
private bool Loading
|
||||
{
|
||||
get
|
||||
{
|
||||
return _loading;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _loading)
|
||||
{
|
||||
_loading = value;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
await OnRefreshAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OnRefreshAsync()
|
||||
{
|
||||
if (LoadData is null || Loading) return;
|
||||
|
||||
Loading = true;
|
||||
StateHasChanged();
|
||||
|
||||
//var start = Stopwatch.GetTimestamp();
|
||||
|
||||
await LoadData();
|
||||
|
||||
//var time = Stopwatch.GetElapsedTime(start);
|
||||
|
||||
//if (time <= TimeSpan.FromSeconds(1))
|
||||
//{
|
||||
// await Task.Delay(TimeSpan.FromSeconds(1).Subtract(time));
|
||||
//}
|
||||
|
||||
Loading = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
@typeparam T
|
||||
|
||||
<PageTitle>@Title</PageTitle>
|
||||
|
||||
<div class="mt-7 mb-6 ml-4">
|
||||
<div class="d-flex mb-3">
|
||||
<div>
|
||||
<MudStack Row="true">
|
||||
<MudBreadcrumbs Items="Breadcrumbs" Style="padding: 0px;" />
|
||||
@if (OnAdd.HasDelegate)
|
||||
{
|
||||
@if (AddBreadcrumbs is not null)
|
||||
{
|
||||
<MudBreadcrumbs Items="AddBreadcrumbs" Class="ml-n3" Style="padding: 0px;" />
|
||||
}
|
||||
|
||||
<MudIconButton OnClick="OnAddClick" Icon="@Icons.Material.Filled.Add" Size="Size.Small" Color="Color.Primary" Class="ml-n4" />
|
||||
}
|
||||
</MudStack>
|
||||
</div>
|
||||
<div class="ml-auto">
|
||||
<MudTextField T="string" Value="@Search" ValueChanged="@(s=>SearchAsync(s))" DebounceInterval="250" Immediate Variant="Variant.Text" Clearable OnBlur="OnSearchReleased"
|
||||
Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Small" Style="margin-top: -15px; width: 40vh;"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<MudTable T="T" @ref="Table" ServerData="OnLoadDataAsync" RowsPerPage="50" Loading="Loading" Elevation="0" Dense Square FixedHeader="true" FixedFooter="true" Height="calc(100vh - 205px)">
|
||||
<HeaderContent>
|
||||
@if (Header is not null)
|
||||
{
|
||||
@Header
|
||||
}
|
||||
<MudTh Style="text-align: right; width: 100px;">
|
||||
<MudStack Row="true" Class="mr-6">
|
||||
<MudButton OnClick="OnFilterClick" Variant="Variant.Text" Size="Size.Small" Color="@(Filtered ? Color.Warning : Color.Inherit)" DisableElevation Disabled="@(!OnFilter.HasDelegate)">
|
||||
Filter
|
||||
</MudButton>
|
||||
<MudButton OnClick="RefreshAsync" Variant="Variant.Text" Size="Size.Small" DisableElevation Disabled="@(DisableRefresh || Loading)">
|
||||
Refresh
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
</MudTh>
|
||||
</HeaderContent>
|
||||
<RowTemplate>
|
||||
@if (RowTemplate is not null)
|
||||
{
|
||||
@RowTemplate(context)
|
||||
}
|
||||
<MudTd DataLabel="Actions" Style="text-align: right;">
|
||||
@if (ActionTemplate is not null)
|
||||
{
|
||||
<MudMenu Label="Actions" Variant="Variant.Text" Size="Size.Small" Dense="true" EndIcon="@Icons.Material.Filled.KeyboardArrowDown" IconColor="Color.Primary"
|
||||
ActivationEvent="@MouseEvent.MouseOver" AnchorOrigin="Origin.BottomRight" FullWidth="true" Disabled="@(DisableAction)">
|
||||
@if (ActionTemplate is not null)
|
||||
{
|
||||
@ActionTemplate(context)
|
||||
}
|
||||
</MudMenu>
|
||||
}
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
<PagerContent>
|
||||
<MudTablePager RowsPerPageString="" />
|
||||
</PagerContent>
|
||||
</MudTable>
|
||||
|
|
@ -0,0 +1,194 @@
|
|||
using Insight.Web.Constants;
|
||||
using Insight.Web.Extensions;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MudBlazor;
|
||||
|
||||
namespace Insight.Web.Components.Containers;
|
||||
|
||||
public partial class TableContainer<T>
|
||||
{
|
||||
[Inject] private NavigationManager NavigationManager { get; init; } = default!;
|
||||
|
||||
|
||||
[Parameter]
|
||||
public string Title { get; set; } = Global.Name;
|
||||
|
||||
[Parameter]
|
||||
public List<BreadcrumbItem>? Breadcrumbs { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<TableState, Task<TableData<T>>>? Data { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment? Header { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment<T>? RowTemplate { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment<T>? ActionTemplate { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool DisableAction { get; set; }
|
||||
|
||||
|
||||
private List<BreadcrumbItem>? AddBreadcrumbs { get; set; }
|
||||
private MudTable<T>? Table { get; set; }
|
||||
private bool Loading { get; set; }
|
||||
|
||||
private int CurrentPage { get; set; }
|
||||
private int CurrentSize { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
if (OnAdd.HasDelegate)
|
||||
{
|
||||
AddBreadcrumbs = new List<BreadcrumbItem>()
|
||||
{
|
||||
new BreadcrumbItem("", "#", true),
|
||||
new BreadcrumbItem("", "#", true)
|
||||
};
|
||||
}
|
||||
|
||||
if (NavigationManager.GetQueryString().TryGetValue("search", out var search))
|
||||
{
|
||||
Search = search.ToString();
|
||||
}
|
||||
|
||||
//if (NavigationManager.GetQueryString().TryGetValue("page", out var pageRaw))
|
||||
//{
|
||||
// if (int.TryParse(pageRaw, out var page))
|
||||
// {
|
||||
// CurrentPage = page;
|
||||
// }
|
||||
//}
|
||||
|
||||
//if (NavigationManager.GetQueryString().TryGetValue("size", out var sizeRaw))
|
||||
//{
|
||||
// if (int.TryParse(sizeRaw, out var size))
|
||||
// {
|
||||
// CurrentSize = size == 0 ? 100 : size;
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
private async Task<TableData<T>> OnLoadDataAsync(TableState state)
|
||||
{
|
||||
if (Data is null)
|
||||
{
|
||||
throw new MissingMethodException(nameof(Data));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Loading = true;
|
||||
|
||||
//state.Page = CurrentPage;
|
||||
//Table.SetRowsPerPage(CurrentSize);
|
||||
|
||||
var data = await Data(state);
|
||||
|
||||
//CurrentPage = state.Page;
|
||||
//CurrentSize = state.PageSize;
|
||||
|
||||
//NavigationManager.ChangeQueryStringValue("page", state.Page.ToString());
|
||||
//NavigationManager.ChangeQueryStringValue("size", state.PageSize.ToString());
|
||||
|
||||
//if (state.SortLabel is not null) NavigationManager.ChangeQueryStringValue("sort", state.SortLabel.ToString());
|
||||
//if (state.SortDirection) NavigationManager.ChangeQueryStringValue("direction", state.SortDirection.ToString());
|
||||
|
||||
return data;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
[Parameter]
|
||||
public bool DisableRefresh { get; set; }
|
||||
|
||||
private string? _search;
|
||||
|
||||
[Parameter]
|
||||
public string? Search
|
||||
{
|
||||
get => _search;
|
||||
set
|
||||
{
|
||||
if (_search == value) return;
|
||||
_search = value;
|
||||
|
||||
SearchChanged.InvokeAsync(value);
|
||||
}
|
||||
}
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<string?> SearchChanged { get; set; }
|
||||
|
||||
private async Task SearchAsync(string? text)
|
||||
{
|
||||
Search = text;
|
||||
|
||||
if (Loading) return;
|
||||
await RefreshAsync();
|
||||
}
|
||||
|
||||
public async Task RefreshAsync()
|
||||
{
|
||||
if (Loading || Table is null) return;
|
||||
|
||||
Loading = true;
|
||||
StateHasChanged();
|
||||
|
||||
//var start = Stopwatch.GetTimestamp();
|
||||
|
||||
await Table.ReloadServerData();
|
||||
|
||||
//var time = Stopwatch.GetElapsedTime(start);
|
||||
|
||||
//if (time <= TimeSpan.FromSeconds(1))
|
||||
//{
|
||||
// await Task.Delay(TimeSpan.FromSeconds(1).Subtract(time));
|
||||
//}
|
||||
|
||||
Loading = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private void OnSearchReleased()
|
||||
{
|
||||
NavigationManager.ChangeQueryStringValue("search", Search);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
[Parameter] public EventCallback OnFilter { get; set; }
|
||||
|
||||
|
||||
[Parameter]
|
||||
public bool Filtered { get; set; }
|
||||
|
||||
private async Task OnFilterClick()
|
||||
{
|
||||
if (OnFilter.HasDelegate)
|
||||
{
|
||||
await OnFilter.InvokeAsync(this);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
[Parameter]
|
||||
public EventCallback OnAdd { get; set; }
|
||||
|
||||
private async Task OnAddClick()
|
||||
{
|
||||
if (OnAdd.HasDelegate)
|
||||
{
|
||||
await OnAdd.InvokeAsync(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/Web/Insight.Web/Components/Dialogs/ActionDialog.razor
Normal file
25
src/Web/Insight.Web/Components/Dialogs/ActionDialog.razor
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
@if (@Actions is not null)
|
||||
{
|
||||
<MudDialog @bind-IsVisible="Visible" Style="max-width: inherit !important;">
|
||||
<DialogContent>
|
||||
<MudStack Class="mt-n3 mb-3">
|
||||
@Content
|
||||
</MudStack>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudStack Row="true" Class="mb-1">
|
||||
@Actions
|
||||
</MudStack>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudDialog @bind-IsVisible="Visible" Style="max-width: inherit !important;">
|
||||
<DialogContent>
|
||||
<MudStack Class="mt-n3 mb-3">
|
||||
@Content
|
||||
</MudStack>
|
||||
</DialogContent>
|
||||
</MudDialog>
|
||||
}
|
||||
30
src/Web/Insight.Web/Components/Dialogs/ActionDialog.razor.cs
Normal file
30
src/Web/Insight.Web/Components/Dialogs/ActionDialog.razor.cs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace Insight.Web.Components.Dialogs;
|
||||
|
||||
public partial class ActionDialog
|
||||
{
|
||||
private bool _visible;
|
||||
|
||||
[Parameter]
|
||||
public bool Visible
|
||||
{
|
||||
get => _visible;
|
||||
set
|
||||
{
|
||||
if (_visible == value) return;
|
||||
_visible = value;
|
||||
|
||||
VisibleChanged.InvokeAsync(value);
|
||||
}
|
||||
}
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<bool> VisibleChanged { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment? Content { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderFragment? Actions { get; set; }
|
||||
}
|
||||
154
src/Web/Insight.Web/Components/Dialogs/ChatDialog.razor
Normal file
154
src/Web/Insight.Web/Components/Dialogs/ChatDialog.razor
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
@using Insight.Web.Models.Account;
|
||||
|
||||
<MudDrawer @bind-Open="_visible" Anchor="Anchor.End" Elevation="0" Variant="@DrawerVariant.Temporary" ClipMode="DrawerClipMode.Always" MiniWidth="400px" Width="@_variableWidth" Style="max-width:auto;">
|
||||
@if (_content == Content.Contacts)
|
||||
{
|
||||
<MudDrawerHeader>
|
||||
<MudText Typo="Typo.h6">
|
||||
<MudLink OnClick="() => _content = Content.Contacts" Underline="Underline.None" Typo="Typo.h6" Color="Color.Dark">
|
||||
Chat
|
||||
</MudLink>
|
||||
</MudText>
|
||||
</MudDrawerHeader>
|
||||
|
||||
@* <MudToolBar Dense="true">
|
||||
<MudText Typo="Typo.h6" Inline="true" Class="mr-2">#</MudText>
|
||||
<MudText Typo="Typo.h6">contacts</MudText>
|
||||
</MudToolBar> *@
|
||||
|
||||
<MudStack Justify="Justify.Center" Class="px-6">
|
||||
<MudList Clickable="true" Style="max-height: calc(100vh - 220px); overflow-y: scroll;">
|
||||
@foreach (var user in ChatService.Users.Keys.Where(p => p.Uid != SessionHandler.State.Uid).OrderByDescending(p => p.Online))
|
||||
{
|
||||
<MudListItem OnClick="@(async () => await GetSessionAsync(user))">
|
||||
<div class="d-flex flex-row mt-n1 mb-n1">
|
||||
<div class="mr-4">
|
||||
<MudBadge Class="my-2" Overlap Visible="true" Color="@(user.Online ? Color.Success : Color.Error)">
|
||||
@* Content="3" *@
|
||||
<MudAvatar Color="Color.Primary" Style="height:50px; width:50px;">
|
||||
@user.Username?.ToUpper().FirstOrDefault()
|
||||
</MudAvatar>
|
||||
</MudBadge>
|
||||
</div>
|
||||
<div>
|
||||
<MudText Typo="Typo.body1" Style="font-weight: normal;">
|
||||
@user.Username
|
||||
</MudText>
|
||||
<MudText Typo="Typo.body1" Style="font-size: x-small!important;">
|
||||
@user.Uid
|
||||
</MudText>
|
||||
</div>
|
||||
</div>
|
||||
</MudListItem>
|
||||
}
|
||||
</MudList>
|
||||
</MudStack>
|
||||
}
|
||||
else if (_content == Content.Chat && _currentSession is not null)
|
||||
{
|
||||
<MudDrawerHeader>
|
||||
<MudText Typo="Typo.h6">
|
||||
<MudLink OnClick="() => _content = Content.Contacts" Underline="Underline.None" Typo="Typo.h6">
|
||||
Chat
|
||||
</MudLink>
|
||||
(@_currentSession.Members.FirstOrDefault(p => p.Uid != SessionHandler.State.Uid)?.Username)
|
||||
<br>
|
||||
<MudText Typo="Typo.body1" Style="font-size: x-small!important;">
|
||||
@_currentSession?.Id.ToString()
|
||||
</MudText>
|
||||
</MudText>
|
||||
</MudDrawerHeader>
|
||||
|
||||
<MudStack Justify="Justify.Center" Class="px-6">
|
||||
<MudList Clickable="false" Style="height: calc(100vh - 260px); overflow-y: scroll;" id="chatContainer">
|
||||
@foreach (var message in _currentSession.Messages.OrderBy(p => p.CreatedDate))
|
||||
{
|
||||
if (message.SenderId == SessionHandler.State.Uid)
|
||||
{
|
||||
<MudListItem Dense="true">
|
||||
<div class="d-flex my-4" style="flex-direction:row-reverse;">
|
||||
<div class="ml-4">
|
||||
<MudAvatar Color="Color.Secondary" Style="height:50px; width:50px;">
|
||||
@SessionHandler.State.Username?.ToUpper().FirstOrDefault()
|
||||
</MudAvatar>
|
||||
</div>
|
||||
<div>
|
||||
<MudText Align="Align.End" Typo="Typo.body1" Style="font-weight: bold;">
|
||||
You
|
||||
</MudText>
|
||||
<MudText Align="Align.End" Typo="Typo.body1" Style="font-size: x-small!important;">
|
||||
@message.CreatedDate.ToString("dd MMM yyyy - hh:mm tt")
|
||||
</MudText>
|
||||
<MudText Align="Align.End" Typo="Typo.body2" Style="padding: 15px; background-color: var(--mud-palette-background-grey); border-radius: 5px; margin-top:5px">
|
||||
@message.Message
|
||||
</MudText>
|
||||
</div>
|
||||
</div>
|
||||
</MudListItem>
|
||||
}
|
||||
else
|
||||
{
|
||||
var sender = ChatService.Users.Keys.FirstOrDefault(p => p.Uid == message.SenderId);
|
||||
|
||||
<MudListItem Dense="true">
|
||||
<div class="d-flex my-4" style="flex-direction:row;">
|
||||
<div class="mr-4">
|
||||
<MudBadge Class="my-2" Overlap Visible="true" Color="@(sender.Online ? Color.Success : Color.Error)">
|
||||
<MudAvatar Color="Color.Primary" Style="height:50px; width:50px;">
|
||||
@sender?.Username?.ToUpper().FirstOrDefault()
|
||||
</MudAvatar>
|
||||
</MudBadge>
|
||||
</div>
|
||||
<div>
|
||||
<MudText Align="Align.Start" Typo="Typo.body1" Style="font-weight: bold;">
|
||||
@sender?.Username
|
||||
</MudText>
|
||||
<MudText Align="Align.Start" Typo="Typo.body1" Style="font-size: x-small!important;">
|
||||
@message.CreatedDate.ToString("dd MMM yyyy - hh:mm tt")
|
||||
</MudText>
|
||||
<MudText Align="Align.Start" Typo="Typo.body2" Style="padding: 15px; background-color: var(--mud-palette-background-grey); border-radius: 5px; margin-top:5px;">
|
||||
@message.Message
|
||||
</MudText>
|
||||
</div>
|
||||
</div>
|
||||
</MudListItem>
|
||||
}
|
||||
}
|
||||
</MudList>
|
||||
|
||||
<MudPaper Elevation="25" Class="d-flex flex-row mx-4" Style="">
|
||||
<MudTextField T="string" @bind-Value="_currentMessage" For="@(()=> _currentMessage)" AutoFocus Immediate Lines="4" TextUpdateSuppression="false" Placeholder="Enter your message..." DisableUnderLine="true" Class="mt-n2 mx-4" OnKeyDown="OnKeyDown" />
|
||||
<MudButton OnClick="SendAsync" Color="Color.Secondary" ButtonType="ButtonType.Button">
|
||||
Send
|
||||
</MudButton>
|
||||
</MudPaper>
|
||||
</MudStack>
|
||||
}
|
||||
</MudDrawer>
|
||||
|
||||
<audio id="chat_user_online" src="/media/chat_user_online.mp3" />
|
||||
<audio id="chat_message" src="/media/chat_message.mp3" />
|
||||
|
||||
@code{
|
||||
private bool _visible;
|
||||
private string _variableWidth => _content switch
|
||||
{
|
||||
Content.Chat => "600px",
|
||||
_ => "400px"
|
||||
};
|
||||
|
||||
public void Toggle()
|
||||
{
|
||||
_visible = !_visible;
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
async void OnKeyDown(KeyboardEventArgs args)
|
||||
{
|
||||
if (args.ShiftKey && args.Key == "Enter")
|
||||
{
|
||||
await Task.Delay(100);
|
||||
await SendAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
197
src/Web/Insight.Web/Components/Dialogs/ChatDialog.razor.cs
Normal file
197
src/Web/Insight.Web/Components/Dialogs/ChatDialog.razor.cs
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
using Insight.Infrastructure.Services;
|
||||
using Insight.Web.Constants;
|
||||
using Insight.Web.Models;
|
||||
using Insight.Web.Services;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using Microsoft.JSInterop;
|
||||
using MongoDB.Driver;
|
||||
using MudBlazor;
|
||||
using Vaitr.Bus;
|
||||
using static Insight.Web.Constants.Events.Chat;
|
||||
|
||||
namespace Insight.Web.Components.Dialogs;
|
||||
|
||||
public partial class ChatDialog
|
||||
{
|
||||
[Inject] private Bus Bus { get; init; } = default!;
|
||||
[Inject] private ChatService ChatService { get; init; } = default!;
|
||||
[Inject] private SessionPool SessionCache { get; init; } = default!;
|
||||
[Inject] private SessionHandler SessionHandler { get; init; } = default!;
|
||||
[Inject] private IdentityService IdentityService { get; init; } = default!;
|
||||
[Inject] private AuthenticationStateProvider AuthenticationStateProvider { get; init; } = default!;
|
||||
[Inject] private IMongoDatabase Database { get; init; } = default!;
|
||||
[Inject] private IJSRuntime JSRuntime { get; init; } = default!;
|
||||
[Inject] private ISnackbar Snackbar { get; init; } = default!;
|
||||
[Inject] private ILogger<ChatDialog> Logger { get; init; } = default!;
|
||||
|
||||
private int _newMessages;
|
||||
public int NewMessages
|
||||
{
|
||||
get
|
||||
{
|
||||
return _newMessages;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != _newMessages)
|
||||
{
|
||||
_newMessages = value;
|
||||
//Bus.Publish(Events.Sessions.Changed); // test, bugt hölle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum Content { Contacts, Chat }
|
||||
private Content _content = Content.Contacts;
|
||||
|
||||
private ChatUser? _currentUser;
|
||||
private ChatSession? _currentSession;
|
||||
private string? _currentMessage;
|
||||
|
||||
private readonly List<IDisposable> _subscriptions = new();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
_subscriptions.Add(Bus.SubscribeAsync<ChatUserConnected>(UserConnected, p => p.User.Uid != SessionHandler.State.Uid));
|
||||
_subscriptions.Add(Bus.SubscribeAsync<ChatUserDisconnected>(UserDisconnected, p => p.User.Uid != SessionHandler.State.Uid));
|
||||
_subscriptions.Add(Bus.SubscribeAsync<ChatRefresh>(RefreshAsync, null));
|
||||
_subscriptions.Add(Bus.SubscribeAsync<ChatMessageReceived>(MessageReceived, p => p.Message.SenderId != SessionHandler.State.Uid));
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentUser = ChatService.Users.FirstOrDefault(p => p.Key.Uid == SessionHandler.State.Uid).Key;
|
||||
|
||||
if (_content == Content.Chat)
|
||||
{
|
||||
await JSRuntime.InvokeAsync<string>("ScrollToBottom", "chatContainer");
|
||||
|
||||
// mock
|
||||
NewMessages = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task GetSessionAsync(ChatUser user)
|
||||
{
|
||||
var members = new List<ChatUser>() { _currentUser, user };
|
||||
var session = await ChatService.AddOrGetSession(members);
|
||||
|
||||
_currentSession = session;
|
||||
|
||||
_content = Content.Chat;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private async Task SendAsync()
|
||||
{
|
||||
if (_currentSession is null || string.IsNullOrWhiteSpace(_currentMessage)) return;
|
||||
|
||||
await _currentSession.SendMessage(_currentUser, _currentMessage, default);
|
||||
_currentMessage = string.Empty;
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
//await JSRuntime.InvokeAsync<string>("ScrollToBottom", "chatContainer");
|
||||
}
|
||||
|
||||
private async ValueTask UserConnected(ChatUserConnected model, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
Notification.Information(Snackbar, $"{model.User.Username} Online", options =>
|
||||
{
|
||||
//options.Onclick = (x) => null;
|
||||
options.HideIcon = true;
|
||||
options.ShowCloseIcon = false;
|
||||
options.RequireInteraction = false;
|
||||
options.ShowTransitionDuration = 0;
|
||||
options.HideTransitionDuration = 1000;
|
||||
options.VisibleStateDuration = 10000;
|
||||
});
|
||||
|
||||
_ = JSRuntime.InvokeAsync<string>("PlayAudio", "chat_user_online");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask UserDisconnected(ChatUserDisconnected model, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
Notification.Information(Snackbar, $"{model.User.Username} Offline", options =>
|
||||
{
|
||||
//options.Onclick = (x) => null;
|
||||
options.HideIcon = true;
|
||||
options.ShowCloseIcon = false;
|
||||
options.RequireInteraction = false;
|
||||
options.ShowTransitionDuration = 0;
|
||||
options.HideTransitionDuration = 1000;
|
||||
options.VisibleStateDuration = 10000;
|
||||
});
|
||||
|
||||
_ = JSRuntime.InvokeAsync<string>("PlayAudio", "chat_user_online");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask RefreshAsync(ChatRefresh model, CancellationToken cancellationToken)
|
||||
{
|
||||
_ = InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private async ValueTask MessageReceived(ChatMessageReceived model, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
Notification.Information(Snackbar, $"New message from: {model.Session.Members.FirstOrDefault(p => p.Uid == model.Message.SenderId)?.Username}", options =>
|
||||
{
|
||||
options.Onclick = (x) => OpenSpecificChatWindow(model.Session);
|
||||
options.HideIcon = true;
|
||||
options.ShowCloseIcon = true;
|
||||
options.RequireInteraction = true;
|
||||
options.ShowTransitionDuration = 0;
|
||||
options.HideTransitionDuration = 0;
|
||||
options.VisibleStateDuration = 10000;
|
||||
options.DuplicatesBehavior = SnackbarDuplicatesBehavior.Prevent;
|
||||
});
|
||||
|
||||
// mock
|
||||
NewMessages++;
|
||||
|
||||
if (_content != Content.Chat)
|
||||
{
|
||||
_ = JSRuntime.InvokeAsync<string>("PlayAudio", "chat_message");
|
||||
}
|
||||
|
||||
_ = InvokeAsync(StateHasChanged);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OpenSpecificChatWindow(ChatSession session)
|
||||
{
|
||||
_currentSession = session;
|
||||
_content = Content.Chat;
|
||||
_visible = true;
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
8
src/Web/Insight.Web/Components/Layouts/LoginLayout.razor
Normal file
8
src/Web/Insight.Web/Components/Layouts/LoginLayout.razor
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
@inherits LayoutComponentBase
|
||||
|
||||
<MudSnackbarProvider />
|
||||
<ThemeProvider DisableIcon />
|
||||
|
||||
<MudLayout>
|
||||
@Body
|
||||
</MudLayout>
|
||||
82
src/Web/Insight.Web/Components/Layouts/MainLayout.razor
Normal file
82
src/Web/Insight.Web/Components/Layouts/MainLayout.razor
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
@using Vaitr.Bus;
|
||||
|
||||
@inherits LayoutComponentBase
|
||||
@implements IDisposable
|
||||
|
||||
@inject Bus Bus
|
||||
|
||||
<MudSnackbarProvider />
|
||||
<MudDialogProvider />
|
||||
|
||||
<MudLayout>
|
||||
<MudAppBar Elevation="0" Fixed>
|
||||
<MudLink Href="@Navigation.Home" Typo="Typo.h6" Underline="Underline.None" Class="mr-4" Style="color: white !important; text-transform:uppercase;">
|
||||
@Global.Name
|
||||
</MudLink>
|
||||
<AuthorizeView>
|
||||
<Authorized>
|
||||
<MudIconButton OnClick="()=>_drawer?.Toggle()" Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit" Edge="Edge.Start" />
|
||||
</Authorized>
|
||||
</AuthorizeView>
|
||||
<MudSpacer />
|
||||
<ChatProvider />
|
||||
<ThemeProvider />
|
||||
<ProfileProvider />
|
||||
</MudAppBar>
|
||||
<AuthorizeView>
|
||||
<Authorized>
|
||||
<CascadingValue Value="RouteValues">
|
||||
<DrawerProvider @ref="_drawer" />
|
||||
</CascadingValue>
|
||||
</Authorized>
|
||||
</AuthorizeView>
|
||||
<MudMainContent>
|
||||
<MudContainer MaxWidth="MaxWidth.ExtraExtraLarge">
|
||||
@Body
|
||||
</MudContainer>
|
||||
</MudMainContent>
|
||||
</MudLayout>
|
||||
|
||||
@code{
|
||||
public IReadOnlyDictionary<string, object>? RouteValues { get; set; }
|
||||
|
||||
private DrawerProvider? _drawer;
|
||||
private bool _disposed;
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
RouteValues = (Body?.Target as RouteView)?.RouteData.RouteValues;
|
||||
|
||||
base.OnParametersSet();
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
await Bus.PublishAsync(Events.Layout.Rendered).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
// auto disposed when starved circuit timeouts (disconnects)
|
||||
if (_disposed is false)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
7
src/Web/Insight.Web/Components/Navbars/Account.razor
Normal file
7
src/Web/Insight.Web/Components/Navbars/Account.razor
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
@inherits ComponentBase
|
||||
|
||||
<MudNavLink Icon="@Icons.Material.Filled.Person" IconColor="Color.Inherit" Match="NavLinkMatch.All" Class="pl-3">
|
||||
Account
|
||||
</MudNavLink>
|
||||
|
||||
<MudNavLink Href=@Navigation.Account.Profile Match="NavLinkMatch.Prefix">Profile</MudNavLink>
|
||||
28
src/Web/Insight.Web/Components/Navbars/Account.razor.cs
Normal file
28
src/Web/Insight.Web/Components/Navbars/Account.razor.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
|
||||
namespace Insight.Web.Components.Navbars;
|
||||
|
||||
public partial class Account
|
||||
{
|
||||
[CascadingParameter] public IReadOnlyDictionary<string, object>? RouteValues { get; set; }
|
||||
|
||||
[Inject] private NavigationManager NavigationManager { get; init; } = default!;
|
||||
[Inject] private AuthenticationStateProvider AuthenticationStateProvider { get; init; } = default!;
|
||||
|
||||
private string Uri { get; set; } = string.Empty;
|
||||
private string Title { get; set; } = "Account";
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var cp = (await AuthenticationStateProvider.GetAuthenticationStateAsync()).User;
|
||||
//Title = $"Account: {cp?.Identity?.Name}";
|
||||
|
||||
Uri = NavigationManager.ToBaseRelativePath(NavigationManager.Uri);
|
||||
}
|
||||
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
{
|
||||
Uri = NavigationManager.ToBaseRelativePath(NavigationManager.Uri);
|
||||
}
|
||||
}
|
||||
13
src/Web/Insight.Web/Components/Navbars/Customer.razor
Normal file
13
src/Web/Insight.Web/Components/Navbars/Customer.razor
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
@inherits ComponentBase
|
||||
|
||||
@*<MudNavLink Icon="@Icons.Material.Outlined.Business" IconColor="Color.Secondary" Match="NavLinkMatch.All" Class="pl-2">
|
||||
@Title
|
||||
</MudNavLink>*@
|
||||
|
||||
<MudNavLink Href=@Navigation.Management.Customers.DetailsHref(Id) Icon="@Icons.Material.Outlined.Business" IconColor="Color.Inherit" Match="NavLinkMatch.All" Class="pl-3">
|
||||
@Title
|
||||
</MudNavLink>
|
||||
|
||||
<div class="mt-3">
|
||||
<MudNavLink Href=@Navigation.Management.Customers.HostsHref(Id) Match="NavLinkMatch.All">Hosts</MudNavLink>
|
||||
</div>
|
||||
62
src/Web/Insight.Web/Components/Navbars/Customer.razor.cs
Normal file
62
src/Web/Insight.Web/Components/Navbars/Customer.razor.cs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
using Insight.Infrastructure;
|
||||
using Insight.Web.Constants;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MongoDB.Driver;
|
||||
using Vaitr.Bus;
|
||||
|
||||
namespace Insight.Web.Components.Navbars;
|
||||
|
||||
public partial class Customer
|
||||
{
|
||||
[CascadingParameter] public IReadOnlyDictionary<string, object>? RouteValues { get; set; }
|
||||
|
||||
[Inject] private IMongoDatabase Database { get; set; } = default!;
|
||||
[Inject] private Bus Bus { get; init; } = default!;
|
||||
|
||||
private string Title { get; set; } = "Customer";
|
||||
private string? Id { get; set; }
|
||||
|
||||
private IDisposable? Token { get; set; }
|
||||
public bool Disposed { get; set; } = false;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Token = Bus.Subscribe<string>(OnRefresh, p => p == Events.Layout.Rendered);
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (RouteValues is not null && Id is null)
|
||||
{
|
||||
if (RouteValues.TryGetValue("customerId", out object? rawId) == false) return;
|
||||
Id = rawId?.ToString();
|
||||
|
||||
var entity = await Database.Customer()
|
||||
.Find(p => p.Id == Id)
|
||||
.FirstOrDefaultAsync(default);
|
||||
|
||||
Title = $"{entity?.Name}";
|
||||
|
||||
await InvokeAsync(() => StateHasChanged());
|
||||
}
|
||||
}
|
||||
|
||||
public void OnRefresh(string? message)
|
||||
{
|
||||
InvokeAsync(() => StateHasChanged());
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (Disposed) return;
|
||||
if (disposing is false) return;
|
||||
|
||||
Token?.Dispose();
|
||||
}
|
||||
}
|
||||
60
src/Web/Insight.Web/Components/Navbars/Host.razor
Normal file
60
src/Web/Insight.Web/Components/Navbars/Host.razor
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
@inherits ComponentBase
|
||||
|
||||
@*@if (CustomerEntity is not null)
|
||||
{
|
||||
<MudNavLink Icon="@Icons.Material.Outlined.Business" Href="@Navigation.Management.Customers.DetailsHref(CustomerEntity.DocumentId.ToString())" Class="pl-2">
|
||||
@CustomerEntity.Name
|
||||
</MudNavLink>
|
||||
}*@
|
||||
|
||||
@if (HostEntity is not null)
|
||||
{
|
||||
<MudNavLink Icon="@Icons.Material.Outlined.Devices" IconColor="Color.Inherit" Href=@Navigation.Management.Hosts.DetailsHref(Id) Match="NavLinkMatch.All" Class="pl-3">
|
||||
@HostEntity.Name
|
||||
</MudNavLink>
|
||||
}
|
||||
|
||||
<div class="mt-3">
|
||||
<MudNavGroup Title="Actions">
|
||||
<MudNavLink Href=@Navigation.Management.Hosts.Actions.Console.IndexHref(Id) Match="NavLinkMatch.Prefix">Console</MudNavLink>
|
||||
</MudNavGroup>
|
||||
</div>
|
||||
|
||||
<div class="mt-1">
|
||||
<MudNavGroup Title="Inventory">
|
||||
<MudNavGroup Title="System">
|
||||
<MudNavLink Href=@Navigation.Management.Hosts.Systems.Os.DetailsHref(Id) Match="NavLinkMatch.Prefix">Os</MudNavLink>
|
||||
<MudNavGroup Title="Updates">
|
||||
<MudNavLink Href=@Navigation.Management.Hosts.Systems.Updates.Installed.IndexHref(Id) Match="NavLinkMatch.Prefix">Installed</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Management.Hosts.Systems.Updates.Pending.IndexHref(Id) Match="NavLinkMatch.Prefix">Pending</MudNavLink>
|
||||
</MudNavGroup>
|
||||
<MudNavLink Href=@Navigation.Management.Hosts.Systems.Sessions.IndexHref(Id) Match="NavLinkMatch.Prefix">Sessions</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Management.Hosts.Systems.Software.IndexHref(Id) Match="NavLinkMatch.Prefix">Software</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Management.Hosts.Systems.Services.IndexHref(Id) Match="NavLinkMatch.Prefix">Services</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Management.Hosts.Systems.Printers.IndexHref(Id) Match="NavLinkMatch.Prefix">Printers</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Management.Hosts.Systems.Volumes.IndexHref(Id) Match="NavLinkMatch.Prefix">Volumes</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Management.Hosts.Systems.Users.IndexHref(Id) Match="NavLinkMatch.Prefix">Users</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Management.Hosts.Systems.Groups.IndexHref(Id) Match="NavLinkMatch.Prefix">Groups</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Management.Hosts.Systems.StoragePools.IndexHref(Id) Match="NavLinkMatch.Prefix">Storage Pools</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Management.Hosts.Systems.VirtualMaschines.IndexHref(Id) Match="NavLinkMatch.Prefix">Virtual Maschines</MudNavLink>
|
||||
</MudNavGroup>
|
||||
<MudNavGroup Title="Network">
|
||||
<MudNavLink Href=@Navigation.Management.Hosts.Network.Interfaces.IndexHref(Id) Match="NavLinkMatch.Prefix">Interfaces</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Management.Hosts.Network.Addresses.IndexHref(Id) Match="NavLinkMatch.Prefix">Addresses</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Management.Hosts.Network.Nameservers.IndexHref(Id) Match="NavLinkMatch.Prefix">Nameservers</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Management.Hosts.Network.Gateways.IndexHref(Id) Match="NavLinkMatch.Prefix">Gateways</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Management.Hosts.Network.Routes.IndexHref(Id) Match="NavLinkMatch.Prefix">Routes</MudNavLink>
|
||||
</MudNavGroup>
|
||||
<MudNavGroup Title="Hardware">
|
||||
<MudNavLink Href=@Navigation.Management.Hosts.Hardware.Mainboard.DetailsHref(Id) Match="NavLinkMatch.Prefix">Mainboard</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Management.Hosts.Hardware.Processors.DetailsHref(Id) Match="NavLinkMatch.Prefix">Processors</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Management.Hosts.Hardware.Memory.IndexHref(Id) Match="NavLinkMatch.Prefix">Memory</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Management.Hosts.Hardware.Drives.IndexHref(Id) Match="NavLinkMatch.Prefix">Drives</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Management.Hosts.Hardware.Videocards.IndexHref(Id) Match="NavLinkMatch.Prefix">Videocards</MudNavLink>
|
||||
</MudNavGroup>
|
||||
</MudNavGroup>
|
||||
</div>
|
||||
|
||||
<div class="mt-1">
|
||||
<MudNavLink Href=@Navigation.Management.Hosts.LogsHref(Id) Match="NavLinkMatch.Prefix">Logs</MudNavLink>
|
||||
</div>
|
||||
69
src/Web/Insight.Web/Components/Navbars/Host.razor.cs
Normal file
69
src/Web/Insight.Web/Components/Navbars/Host.razor.cs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
using Insight.Infrastructure;
|
||||
using Insight.Infrastructure.Entities;
|
||||
using Insight.Web.Constants;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MongoDB.Driver;
|
||||
using Vaitr.Bus;
|
||||
|
||||
namespace Insight.Web.Components.Navbars;
|
||||
|
||||
public partial class Host : IDisposable
|
||||
{
|
||||
[CascadingParameter] public IReadOnlyDictionary<string, object>? RouteValues { get; set; }
|
||||
|
||||
[Inject] private IMongoDatabase Database { get; init; } = default!;
|
||||
[Inject] private Bus Bus { get; init; } = default!;
|
||||
|
||||
private CustomerEntity? CustomerEntity { get; set; }
|
||||
private HostEntity HostEntity { get; set; }
|
||||
private string? Id { get; set; }
|
||||
|
||||
private IDisposable? Token { get; set; }
|
||||
public bool Disposed { get; set; } = false;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Token = Bus?.Subscribe<string>(OnRefresh, p => p == Events.Layout.Rendered);
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (RouteValues is not null && Id is null)
|
||||
{
|
||||
if (RouteValues.TryGetValue("hostId", out object? rawId) == false) return;
|
||||
Id = rawId?.ToString();
|
||||
|
||||
HostEntity = await Database.Host()
|
||||
.Find(p => p.Id == Id)
|
||||
.FirstOrDefaultAsync(default);
|
||||
|
||||
if (HostEntity is not null)
|
||||
{
|
||||
CustomerEntity = await Database.Customer()
|
||||
.Find(p => p.Id == HostEntity.Customer)
|
||||
.FirstOrDefaultAsync(default);
|
||||
}
|
||||
|
||||
await InvokeAsync(() => StateHasChanged());
|
||||
}
|
||||
}
|
||||
|
||||
public void OnRefresh(string? message)
|
||||
{
|
||||
InvokeAsync(() => StateHasChanged());
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (Disposed) return;
|
||||
if (disposing is false) return;
|
||||
|
||||
Token?.Dispose();
|
||||
}
|
||||
}
|
||||
112
src/Web/Insight.Web/Components/Navbars/Main.razor
Normal file
112
src/Web/Insight.Web/Components/Navbars/Main.razor
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
@inherits ComponentBase
|
||||
|
||||
<MudNavGroup Title="Monitoring" Expanded="ExpandMonitoringGroup">
|
||||
@*<MudNavLink Href=@Navigation.Monitoring.Index Match="NavLinkMatch.Prefix">
|
||||
Dashboard
|
||||
</MudNavLink>*@
|
||||
<MudNavLink Href=@Navigation.Monitoring.Maintenance.Index Match="NavLinkMatch.Prefix">
|
||||
Maintenance
|
||||
</MudNavLink>
|
||||
@*<MudNavLink Href=@Navigation.Monitoring.Problems Match="NavLinkMatch.Prefix">
|
||||
Problems
|
||||
</MudNavLink>*@
|
||||
</MudNavGroup>
|
||||
|
||||
<MudNavGroup Title="Inventory" Expanded="ExpandInventoryGroup">
|
||||
<MudNavGroup Title="System" Expanded="ExpandInventorySystem">
|
||||
<MudNavLink Href=@Navigation.Inventory.Systems.Os.Index Match="NavLinkMatch.Prefix">
|
||||
Os
|
||||
</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Inventory.Systems.Updates.Index Match="NavLinkMatch.Prefix">
|
||||
Updates
|
||||
</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Inventory.Systems.Software.Index Match="NavLinkMatch.Prefix">
|
||||
Software
|
||||
</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Inventory.Systems.Services.Index Match="NavLinkMatch.Prefix">
|
||||
Services
|
||||
</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Inventory.Systems.Sessions.Index Match="NavLinkMatch.Prefix">
|
||||
Sessions
|
||||
</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Inventory.Systems.Printers.Index Match="NavLinkMatch.Prefix">
|
||||
Printers
|
||||
</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Inventory.Systems.Volumes.Index Match="NavLinkMatch.Prefix">
|
||||
Volumes
|
||||
</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Inventory.Systems.Users.Index Match="NavLinkMatch.Prefix">
|
||||
Users
|
||||
</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Inventory.Systems.Groups.Index Match="NavLinkMatch.Prefix">
|
||||
Groups
|
||||
</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Inventory.Systems.StoragePools.Index Match="NavLinkMatch.Prefix">
|
||||
Storage Pools
|
||||
</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Inventory.Systems.VirtualMaschines.Index Match="NavLinkMatch.Prefix">
|
||||
Virtual Maschines
|
||||
</MudNavLink>
|
||||
</MudNavGroup>
|
||||
<MudNavGroup Title="Network" Expanded="ExpandInventoryNetwork">
|
||||
<MudNavLink Href=@Navigation.Inventory.Network.Interfaces.Index Match="NavLinkMatch.Prefix">
|
||||
Interfaces
|
||||
</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Inventory.Network.Addresses.Index Match="NavLinkMatch.Prefix">
|
||||
Addresses
|
||||
</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Inventory.Network.Nameservers.Index Match="NavLinkMatch.Prefix">
|
||||
Nameservers
|
||||
</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Inventory.Network.Gateways.Index Match="NavLinkMatch.Prefix">
|
||||
Gateways
|
||||
</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Inventory.Network.Routes.Index Match="NavLinkMatch.Prefix">
|
||||
Routes
|
||||
</MudNavLink>
|
||||
</MudNavGroup>
|
||||
<MudNavGroup Title="Hardware" Expanded="ExpandInventoryHardware">
|
||||
<MudNavLink Href=@Navigation.Inventory.Hardware.Mainboards.Index Match="NavLinkMatch.Prefix">
|
||||
Mainboards
|
||||
</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Inventory.Hardware.Processors.Index Match="NavLinkMatch.Prefix">
|
||||
Processors
|
||||
</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Inventory.Hardware.Memory.Index Match="NavLinkMatch.Prefix">
|
||||
Memory
|
||||
</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Inventory.Hardware.Drives.Index Match="NavLinkMatch.Prefix">
|
||||
Drives
|
||||
</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Inventory.Hardware.Videocards.Index Match="NavLinkMatch.Prefix">
|
||||
Videocards
|
||||
</MudNavLink>
|
||||
</MudNavGroup>
|
||||
</MudNavGroup>
|
||||
|
||||
<MudNavGroup Title="Management" Expanded="ExpandManagementGroup">
|
||||
<MudNavLink Href=@Navigation.Management.Overview.Index Match="NavLinkMatch.Prefix">
|
||||
Overview
|
||||
</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Management.Accounts.Index Match="NavLinkMatch.Prefix">
|
||||
Accounts
|
||||
</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Management.Customers.Index Match="NavLinkMatch.Prefix">
|
||||
Customers
|
||||
</MudNavLink>
|
||||
<MudNavLink Href=@Navigation.Management.Hosts.Index Match="NavLinkMatch.Prefix">
|
||||
Hosts
|
||||
</MudNavLink>
|
||||
@*<MudNavLink Href=@Navigation.Management.HostGroups.Index Match="NavLinkMatch.Prefix">
|
||||
Host Groups
|
||||
</MudNavLink>*@
|
||||
<MudNavLink Href=@Navigation.Management.Agents.Index Match="NavLinkMatch.Prefix">
|
||||
Agents
|
||||
</MudNavLink>
|
||||
</MudNavGroup>
|
||||
|
||||
@*<MudNavGroup Title="Test">
|
||||
<MudNavLink Href="chat" Match="NavLinkMatch.Prefix">
|
||||
Chat
|
||||
</MudNavLink>
|
||||
</MudNavGroup>*@
|
||||
136
src/Web/Insight.Web/Components/Navbars/Main.razor.cs
Normal file
136
src/Web/Insight.Web/Components/Navbars/Main.razor.cs
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
using Insight.Web.Constants;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Vaitr.Bus;
|
||||
|
||||
namespace Insight.Web.Components.Navbars;
|
||||
|
||||
public partial class Main : IDisposable
|
||||
{
|
||||
[Inject] private Bus Bus { get; init; } = default!;
|
||||
[Inject] private NavigationManager NavigationManager { get; init; } = default!;
|
||||
|
||||
private string Uri { get; set; } = string.Empty;
|
||||
private IDisposable? Token { get; set; }
|
||||
public bool Disposed { get; set; } = false;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Token = Bus?.Subscribe<string>(OnRefresh, p => p == Events.Layout.Rendered);
|
||||
OnRefresh();
|
||||
}
|
||||
|
||||
public void OnRefresh(string? message = null)
|
||||
{
|
||||
Uri = NavigationManager.ToBaseRelativePath(NavigationManager.Uri);
|
||||
InvokeAsync(() => StateHasChanged());
|
||||
}
|
||||
|
||||
private bool ExpandMonitoringGroup
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Uri.StartsWith(Navigation.Monitoring.Index)) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool ExpandManagementGroup
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Uri.StartsWith(Navigation.Management.Overview.Index)) return true;
|
||||
if (Uri.StartsWith(Navigation.Management.Accounts.Index)) return true;
|
||||
if (Uri.StartsWith(Navigation.Management.Customers.Index)) return true;
|
||||
if (Uri.StartsWith(Navigation.Management.Hosts.Index)) return true;
|
||||
if (Uri.StartsWith(Navigation.Management.HostGroups.Index)) return true;
|
||||
if (Uri.StartsWith(Navigation.Management.Agents.Index)) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool ExpandInventoryGroup
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ExpandInventorySystem) return true;
|
||||
if (ExpandInventoryNetwork) return true;
|
||||
if (ExpandInventoryHardware) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool ExpandInventorySystem
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Uri.StartsWith(Navigation.Inventory.Systems.Os.Index)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Systems.Os.Hosts)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Systems.Os.Guests)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Systems.Updates.Index)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Systems.Updates.Hosts)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Systems.Software.Index)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Systems.Software.Hosts)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Systems.Services.Index)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Systems.Services.Hosts)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Systems.Sessions.Index)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Systems.Printers.Index)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Systems.Volumes.Index)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Systems.Users.Index)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Systems.Users.Hosts)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Systems.Groups.Index)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Systems.Groups.Hosts)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Systems.StoragePools.Index)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Systems.VirtualMaschines.Index)) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool ExpandInventoryNetwork
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Uri.StartsWith(Navigation.Inventory.Network.Interfaces.Index)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Network.Addresses.Index)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Network.Addresses.Hosts)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Network.Nameservers.Index)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Network.Nameservers.Hosts)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Network.Gateways.Index)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Network.Gateways.Hosts)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Network.Routes.Index)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Network.Routes.Hosts)) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool ExpandInventoryHardware
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Uri.StartsWith(Navigation.Inventory.Hardware.Mainboards.Index)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Hardware.Mainboards.Hosts)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Hardware.Processors.Index)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Hardware.Processors.Hosts)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Hardware.Memory.Index)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Hardware.Memory.Hosts)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Hardware.Drives.Index)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Hardware.Drives.Hosts)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Hardware.Videocards.Index)) return true;
|
||||
if (Uri.StartsWith(Navigation.Inventory.Hardware.Videocards.Hosts)) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (Disposed) return;
|
||||
if (disposing is false) return;
|
||||
|
||||
Token?.Dispose();
|
||||
}
|
||||
}
|
||||
29
src/Web/Insight.Web/Components/Navbars/NavSwitch.razor
Normal file
29
src/Web/Insight.Web/Components/Navbars/NavSwitch.razor
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
@using System.Reflection;
|
||||
@inherits ComponentBase
|
||||
|
||||
<div class="mt-5">
|
||||
<MudNavMenu>
|
||||
<CascadingValue Value="RouteValues">
|
||||
@if (_content == Content.Account)
|
||||
{
|
||||
<Account />
|
||||
}
|
||||
else if (_content == Content.Host)
|
||||
{
|
||||
<Host />
|
||||
}
|
||||
else if (_content == Content.Customer)
|
||||
{
|
||||
<Customer />
|
||||
}
|
||||
else
|
||||
{
|
||||
<Main />
|
||||
}
|
||||
</CascadingValue>
|
||||
</MudNavMenu>
|
||||
</div>
|
||||
|
||||
<MudText Typo="Typo.subtitle1" Class="d-flex flex-wrap flex-grow-1 align-content-end justify-center mb-2" Style="opacity: 0.2;">
|
||||
@(Assembly.GetEntryAssembly()?.GetName().Version)
|
||||
</MudText>
|
||||
59
src/Web/Insight.Web/Components/Navbars/NavSwitch.razor.cs
Normal file
59
src/Web/Insight.Web/Components/Navbars/NavSwitch.razor.cs
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
using Insight.Web.Constants;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Vaitr.Bus;
|
||||
|
||||
namespace Insight.Web.Components.Navbars;
|
||||
|
||||
public partial class NavSwitch : IDisposable
|
||||
{
|
||||
[CascadingParameter] public IReadOnlyDictionary<string, object>? RouteValues { get; set; }
|
||||
|
||||
[Inject] private NavigationManager NavigationManager { get; init; } = default!;
|
||||
[Inject] private Bus Bus { get; init; } = default!;
|
||||
|
||||
public string Url { get; set; } = string.Empty;
|
||||
private IDisposable? Token { get; set; }
|
||||
public bool Disposed { get; set; } = false;
|
||||
|
||||
private enum Content { Main, Account, Customer, Host }
|
||||
private Content _content = Content.Main;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Token = Bus?.SubscribeAsync<string>(OnRefreshAsync, p => p == Events.Layout.Rendered);
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
await OnRefreshAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask OnRefreshAsync(string? message = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Url = NavigationManager.ToBaseRelativePath(NavigationManager.Uri);
|
||||
|
||||
if (Url is null) return;
|
||||
|
||||
if (Url.StartsWith($"{Navigation.Account.Index}/")) _content = Content.Account;
|
||||
else if (Url.StartsWith($"{Navigation.Management.Customers.Index}/")) _content = Content.Customer;
|
||||
else if (Url.StartsWith($"{Navigation.Management.Hosts.Index}/")) _content = Content.Host;
|
||||
else _content = Content.Main;
|
||||
|
||||
await InvokeAsync(StateHasChanged).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (Disposed) return;
|
||||
Token?.Dispose();
|
||||
}
|
||||
}
|
||||
49
src/Web/Insight.Web/Components/Providers/ChatProvider.razor
Normal file
49
src/Web/Insight.Web/Components/Providers/ChatProvider.razor
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
@using System.Collections.Concurrent;
|
||||
@using Vaitr.Bus;
|
||||
@using static Insight.Web.Constants.Events.Chat;
|
||||
|
||||
@inject Bus Bus
|
||||
@inject SessionHandler SessionHandler
|
||||
|
||||
@implements IDisposable
|
||||
|
||||
<AuthorizeView>
|
||||
<Authorized>
|
||||
<MudIconButton OnClick="()=>_chatDialog?.Toggle()" Icon="@(_chatDialog?.NewMessages > 0 ? Icons.Material.Filled.MarkUnreadChatAlt : Icons.Material.Filled.Chat)" Color="Color.Inherit" />
|
||||
<ChatDialog @ref="_chatDialog" />
|
||||
</Authorized>
|
||||
</AuthorizeView>
|
||||
|
||||
@code {
|
||||
private ChatDialog? _chatDialog;
|
||||
private bool _disposed;
|
||||
|
||||
private readonly ConcurrentBag<IDisposable> _subscriptions = new();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
_subscriptions.Add(Bus.SubscribeAsync<string>(async (x, c) => await InvokeAsync(StateHasChanged), p => p == Events.Sessions.Changed));
|
||||
_subscriptions.Add(Bus.SubscribeAsync<ChatMessageReceived>(async (x, c) => await InvokeAsync(StateHasChanged), p => p.Message.SenderId != SessionHandler.State.Uid));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
// auto disposed when starved circuit timeouts (disconnects)
|
||||
if (_disposed is false)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
foreach (var sub in _subscriptions) sub.Dispose();
|
||||
_subscriptions.Clear();
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<CascadingValue Value="RouteValues">
|
||||
<MudDrawer @bind-Open="@_open" ClipMode="DrawerClipMode.Docked" Variant="DrawerVariant.Responsive" Breakpoint="Breakpoint.Md" Elevation="2">
|
||||
<AuthorizeView>
|
||||
<Authorized>
|
||||
<NavSwitch />
|
||||
</Authorized>
|
||||
</AuthorizeView>
|
||||
</MudDrawer>
|
||||
</CascadingValue>
|
||||
|
||||
@code {
|
||||
[CascadingParameter] public IReadOnlyDictionary<string, object>? RouteValues { get; set; }
|
||||
|
||||
private bool _open = true;
|
||||
|
||||
public void Toggle() => _open = !_open;
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
@inject NavigationManager NavigationManager
|
||||
|
||||
<AuthorizeView>
|
||||
<Authorized>
|
||||
<MudMenu @ref="_menu" ActivationEvent="@MouseEvent.LeftClick" Dense="true" Class="ml-4" AnchorOrigin="Origin.TopRight" TransformOrigin="Origin.TopRight">
|
||||
<ActivatorContent>
|
||||
@if (string.IsNullOrEmpty(""))
|
||||
{
|
||||
<MudAvatar Square Variant="Variant.Filled" Color="Color.Dark">
|
||||
@context.User.Identity?.Name?.FirstOrDefault()
|
||||
</MudAvatar>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudImage Src="" />
|
||||
}
|
||||
</ActivatorContent>
|
||||
<ChildContent>
|
||||
<MudCard Elevation="0" Square="true" Class="mt-n2">
|
||||
<MudCardHeader>
|
||||
<CardHeaderAvatar>
|
||||
@if (string.IsNullOrEmpty(""))
|
||||
{
|
||||
<MudAvatar Square Variant="Variant.Filled" Color="Color.Dark">
|
||||
@context.User.Identity?.Name?.FirstOrDefault()
|
||||
</MudAvatar>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudAvatar Image="" />
|
||||
}
|
||||
</CardHeaderAvatar>
|
||||
<CardHeaderContent>
|
||||
<MudText Typo="Typo.body2">@context.User.Identity?.Name</MudText>
|
||||
@*<MudText Typo="Typo.caption">@context.User.Identity?.Name!</MudText>*@
|
||||
</CardHeaderContent>
|
||||
</MudCardHeader>
|
||||
</MudCard>
|
||||
@*<MudDivider Class="mb-2" />*@
|
||||
<MudListItem Text="Profile" OnClick="@(s=>OnProfile())" Icon="@Icons.Material.Outlined.Person" />
|
||||
<MudListItem Text="Logout" OnClick="@(s=>OnLogout())" Icon="@Icons.Material.Filled.Logout" />
|
||||
</ChildContent>
|
||||
</MudMenu>
|
||||
</Authorized>
|
||||
<NotAuthorized>
|
||||
<MudIconButton Icon="@Icons.Material.Outlined.Login" Color="Color.Inherit" Class="ml-3" OnClick="@(s=>OnLogin())" />
|
||||
</NotAuthorized>
|
||||
</AuthorizeView>
|
||||
|
||||
@code{
|
||||
private MudMenu? _menu;
|
||||
|
||||
private void OnLogin()
|
||||
{
|
||||
_menu?.CloseMenu();
|
||||
NavigationManager.NavigateTo(Navigation.Account.Login, false);
|
||||
}
|
||||
|
||||
private void OnLogout()
|
||||
{
|
||||
_menu?.CloseMenu();
|
||||
NavigationManager.NavigateTo(Navigation.Account.Logout, true);
|
||||
}
|
||||
|
||||
private void OnProfile()
|
||||
{
|
||||
_menu?.CloseMenu();
|
||||
NavigationManager.NavigateTo(Navigation.Account.Profile, false);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
@using Vaitr.Bus;
|
||||
|
||||
@inject SessionHandler SessionHandler
|
||||
@inject Bus Bus
|
||||
|
||||
@code {
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
await SessionHandler.UpdateStateAsync(default);
|
||||
}
|
||||
}
|
||||
}
|
||||
90
src/Web/Insight.Web/Components/Providers/ThemeProvider.razor
Normal file
90
src/Web/Insight.Web/Components/Providers/ThemeProvider.razor
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
@using Blazored.LocalStorage;
|
||||
@using Microsoft.AspNetCore.Identity;
|
||||
@using MongoDB.Driver;
|
||||
@using Insight.Infrastructure
|
||||
|
||||
@inject IServiceScopeFactory ServiceScopeFactory
|
||||
@inject ILocalStorageService LocalStorageService
|
||||
@inject IMongoDatabase Database
|
||||
@inject AuthenticationStateProvider AuthenticationStateProvider
|
||||
|
||||
<MudThemeProvider Theme="CurrentTheme" IsDarkMode="DarkMode" />
|
||||
|
||||
@if (DisableIcon is false)
|
||||
{
|
||||
<MudIconButton OnClick="@OnDarkModeToggleAsync" Icon="@(DarkMode ? Icons.Material.Filled.Brightness5 : Icons.Material.Filled.Brightness4)" Color="Color.Inherit" />
|
||||
}
|
||||
|
||||
@code {
|
||||
[Parameter] public bool DisableIcon { get; set; }
|
||||
|
||||
private MudTheme CurrentTheme { get; } = Themes.Default();
|
||||
private bool DarkMode { get; set; }
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await LoadDarkModeAsync();
|
||||
}
|
||||
|
||||
public async Task LoadDarkModeAsync()
|
||||
{
|
||||
// local storage
|
||||
var storageDarkMode = await LocalStorageService.GetItemAsync<bool?>("darkmode");
|
||||
if (storageDarkMode is bool darkmodeValue) DarkMode = darkmodeValue;
|
||||
|
||||
// database override
|
||||
var state = await AuthenticationStateProvider.GetAuthenticationStateAsync();
|
||||
|
||||
if (state?.User.Identity is not null && state.User.Identity.IsAuthenticated)
|
||||
{
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<InsightUser>>();
|
||||
|
||||
if (await userManager.FindByNameAsync(state.User.Identity.Name) is not InsightUser user) return;
|
||||
|
||||
var userPrefs = await Database.UserPreference()
|
||||
.Find(p => p.User == user.Id.ToString())
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (userPrefs is null) return;
|
||||
|
||||
DarkMode = userPrefs.DarkMode;
|
||||
|
||||
await LocalStorageService.SetItemAsync("darkmode", DarkMode);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OnDarkModeToggleAsync()
|
||||
{
|
||||
// update current
|
||||
DarkMode = !DarkMode;
|
||||
|
||||
// update local storage
|
||||
await LocalStorageService.SetItemAsync("darkmode", DarkMode);
|
||||
|
||||
// update database
|
||||
var state = await AuthenticationStateProvider.GetAuthenticationStateAsync();
|
||||
|
||||
if (state?.User.Identity is not null && state.User.Identity.IsAuthenticated)
|
||||
{
|
||||
using var scope = ServiceScopeFactory.CreateScope();
|
||||
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<InsightUser>>();
|
||||
|
||||
if (await userManager.FindByNameAsync(state.User.Identity.Name) is not InsightUser user) return;
|
||||
|
||||
var date = DateTime.Now;
|
||||
|
||||
var userPrefs = await Database.UserPreference()
|
||||
.UpdateOneAsync(p => p.User == user.Id.ToString(), Builders<InsightUserPreferences>.Update
|
||||
.SetOnInsert(p => p.User, user.Id.ToString())
|
||||
.SetOnInsert(p => p.Insert, date)
|
||||
.Set(p => p.Update, date)
|
||||
.Set(p => p.DarkMode, DarkMode), new UpdateOptions
|
||||
{
|
||||
IsUpsert = true
|
||||
});
|
||||
}
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
24
src/Web/Insight.Web/Constants/Events.cs
Normal file
24
src/Web/Insight.Web/Constants/Events.cs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
using Insight.Web.Models;
|
||||
|
||||
namespace Insight.Web.Constants;
|
||||
|
||||
public static class Events
|
||||
{
|
||||
public static class Sessions
|
||||
{
|
||||
public const string Changed = "sessions.changed";
|
||||
}
|
||||
|
||||
public static class Layout
|
||||
{
|
||||
public const string Rendered = "layout.rendered";
|
||||
}
|
||||
|
||||
public static class Chat
|
||||
{
|
||||
public record ChatUserConnected(ChatUser User);
|
||||
public record ChatUserDisconnected(ChatUser User);
|
||||
public record ChatRefresh();
|
||||
public record ChatMessageReceived(ChatSession Session, ChatMessage Message);
|
||||
}
|
||||
}
|
||||
7
src/Web/Insight.Web/Constants/Global.cs
Normal file
7
src/Web/Insight.Web/Constants/Global.cs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
namespace Insight.Web.Constants;
|
||||
|
||||
public static class Global
|
||||
{
|
||||
public const string Name = "Insight";
|
||||
public const string NotFound = "Not Found|Insight";
|
||||
}
|
||||
747
src/Web/Insight.Web/Constants/Navigation.cs
Normal file
747
src/Web/Insight.Web/Constants/Navigation.cs
Normal file
|
|
@ -0,0 +1,747 @@
|
|||
namespace Insight.Web.Constants;
|
||||
|
||||
public static class Navigation
|
||||
{
|
||||
public const string Home = "";
|
||||
|
||||
public static class Internal
|
||||
{
|
||||
public const string Sessions = "internal/sessions";
|
||||
public const string Seed = "internal/seed";
|
||||
}
|
||||
|
||||
public static class Account
|
||||
{
|
||||
public const string Index = "account";
|
||||
public const string Login = "account/login";
|
||||
public const string LoginTFA = "account/login/{key:guid}";
|
||||
public const string SignIn = "account/signin";
|
||||
public const string SignInTFA = "account/signin/2fa";
|
||||
public const string Logout = "account/logout";
|
||||
public const string Lockout = "account/lockout";
|
||||
public const string Profile = "account/profile";
|
||||
public const string ChangePassword = "account/changepassword";
|
||||
|
||||
public static string LoginHref(string redirect)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(redirect))
|
||||
{
|
||||
return Login;
|
||||
}
|
||||
|
||||
return $"{Login}?redirect={redirect}";
|
||||
}
|
||||
|
||||
public static string LoginTFAHref(Guid key)
|
||||
{
|
||||
return LoginTFA.Replace("{key:guid}", key.ToString());
|
||||
}
|
||||
|
||||
public static string SignInHref(Guid key)
|
||||
{
|
||||
return $"{SignIn}?key={key}";
|
||||
}
|
||||
|
||||
public static string SignInTFAHref(Guid key)
|
||||
{
|
||||
return $"{SignInTFA}?key={key}";
|
||||
}
|
||||
|
||||
public static string ChangePasswordHref(Guid key)
|
||||
{
|
||||
return $"{ChangePassword}?key={key}";
|
||||
}
|
||||
}
|
||||
|
||||
public static class Monitoring
|
||||
{
|
||||
public const string Index = "monitoring";
|
||||
|
||||
public static class Maintenance
|
||||
{
|
||||
public const string Index = "monitoring/maintenance";
|
||||
|
||||
public static class Drives
|
||||
{
|
||||
public const string Index = "monitoring/maintenance/drives";
|
||||
}
|
||||
|
||||
public static class StoragePools
|
||||
{
|
||||
public const string Index = "monitoring/maintenance/storagepools";
|
||||
}
|
||||
|
||||
public static class Volumes
|
||||
{
|
||||
public const string Index = "monitoring/maintenance/volumes";
|
||||
}
|
||||
|
||||
public static class Guests
|
||||
{
|
||||
public const string Index = "monitoring/maintenance/guests";
|
||||
}
|
||||
|
||||
public static class Snapshots
|
||||
{
|
||||
public const string Index = "monitoring/maintenance/snapshots";
|
||||
}
|
||||
|
||||
public static class Updates
|
||||
{
|
||||
public const string Index = "monitoring/maintenance/updates";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Management
|
||||
{
|
||||
public const string Index = "management";
|
||||
|
||||
public static class Overview
|
||||
{
|
||||
public const string Index = "management/overview";
|
||||
}
|
||||
|
||||
public static class Accounts
|
||||
{
|
||||
public const string Index = "management/accounts";
|
||||
public const string Details = "management/accounts/{accountId}";
|
||||
|
||||
public static string DetailsHref(string? accountId)
|
||||
{
|
||||
return Details.Replace("{accountId}", accountId);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Customers
|
||||
{
|
||||
public const string Index = "management/customers";
|
||||
public const string Details = "management/customers/{customerId}";
|
||||
public const string Hosts = "management/customers/{customerId}/hosts";
|
||||
public const string HostsAssign = "management/customers/{customerId}/hosts/assign";
|
||||
|
||||
public static string DetailsHref(string? customerId)
|
||||
{
|
||||
return Details.Replace("{customerId}", customerId);
|
||||
}
|
||||
|
||||
public static string HostsHref(string? customerId)
|
||||
{
|
||||
return Hosts.Replace("{customerId}", customerId);
|
||||
}
|
||||
|
||||
public static string HostsAssignHref(string? customerId)
|
||||
{
|
||||
return HostsAssign.Replace("{customerId}", customerId);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Agents
|
||||
{
|
||||
public const string Index = "management/agents";
|
||||
public const string Details = "management/agents/{agentId}";
|
||||
public const string Logs = "management/agents/{agentId}/logs";
|
||||
public const string HostAssign = "management/agents/{agentId}/assign";
|
||||
|
||||
public static string DetailsHref(string? agentId)
|
||||
{
|
||||
return Details.Replace("{agentId}", agentId);
|
||||
}
|
||||
|
||||
public static string LogsHref(string? agentId)
|
||||
{
|
||||
return Logs.Replace("{agentId}", agentId);
|
||||
}
|
||||
|
||||
public static string HostAssingHref(string? agentId)
|
||||
{
|
||||
return HostAssign.Replace("{agentId}", agentId);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Hosts
|
||||
{
|
||||
public const string Index = "management/hosts";
|
||||
public const string Details = "management/hosts/{hostId}";
|
||||
public const string Logs = "management/hosts/{hostId}/logs";
|
||||
public const string CustomerAssign = "management/hosts/{hostId}/customer/assign";
|
||||
public const string AgentAssign = "management/hosts/{hostId}/agent/assign";
|
||||
|
||||
public static string DetailsHref(string? hostId)
|
||||
{
|
||||
return Details.Replace("{hostId}", hostId);
|
||||
}
|
||||
|
||||
public static string LogsHref(string? hostId)
|
||||
{
|
||||
return Logs.Replace("{hostId}", hostId);
|
||||
}
|
||||
|
||||
public static string CustomerAssingHref(string? hostId)
|
||||
{
|
||||
return CustomerAssign.Replace("{hostId}", hostId);
|
||||
}
|
||||
|
||||
public static string AgentAssingHref(string? hostId)
|
||||
{
|
||||
return AgentAssign.Replace("{hostId}", hostId);
|
||||
}
|
||||
|
||||
public static class Actions
|
||||
{
|
||||
public static class Console
|
||||
{
|
||||
public const string Index = "management/hosts/{hostId}/console";
|
||||
|
||||
public static string IndexHref(string? hostId)
|
||||
{
|
||||
return Index.Replace("{hostId}", hostId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Systems
|
||||
{
|
||||
public static class Os
|
||||
{
|
||||
public const string Details = "management/hosts/{hostId}/os";
|
||||
|
||||
public static string DetailsHref(string? hostId)
|
||||
{
|
||||
return Details.Replace("{hostId}", hostId);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Updates
|
||||
{
|
||||
public static class Installed
|
||||
{
|
||||
public const string Index = "management/hosts/{hostId}/updates/installed";
|
||||
|
||||
public static string IndexHref(string? hostId)
|
||||
{
|
||||
return Index.Replace("{hostId}", hostId);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Pending
|
||||
{
|
||||
public const string Index = "management/hosts/{hostId}/updates/pending";
|
||||
|
||||
public static string IndexHref(string? hostId)
|
||||
{
|
||||
return Index.Replace("{hostId}", hostId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Sessions
|
||||
{
|
||||
public const string Index = "management/hosts/{hostId}/sessions";
|
||||
|
||||
public static string IndexHref(string? hostId)
|
||||
{
|
||||
return Index.Replace("{hostId}", hostId);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Software
|
||||
{
|
||||
public const string Index = "management/hosts/{hostId}/software";
|
||||
|
||||
public static string IndexHref(string? hostId)
|
||||
{
|
||||
return Index.Replace("{hostId}", hostId);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Services
|
||||
{
|
||||
public const string Index = "management/hosts/{hostId}/services";
|
||||
|
||||
public static string IndexHref(string? hostId)
|
||||
{
|
||||
return Index.Replace("{hostId}", hostId);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Printers
|
||||
{
|
||||
public const string Index = "management/hosts/{hostId}/printers";
|
||||
|
||||
public static string IndexHref(string? hostId)
|
||||
{
|
||||
return Index.Replace("{hostId}", hostId);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Volumes
|
||||
{
|
||||
public const string Index = "management/hosts/{hostId}/volumes";
|
||||
public const string Details = "management/hosts/{hostId}/volumes/{volumeId}";
|
||||
|
||||
public static string IndexHref(string? hostId)
|
||||
{
|
||||
return Index.Replace("{hostId}", hostId);
|
||||
}
|
||||
|
||||
public static string DetailsHref(string? hostId, string? volumeId)
|
||||
{
|
||||
return Details.Replace("{hostId}", hostId).Replace("{volumeId}", volumeId);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Users
|
||||
{
|
||||
public const string Index = "management/hosts/{hostId}/users";
|
||||
public const string Details = "management/hosts/{hostId}/users/{userId}";
|
||||
|
||||
public static string IndexHref(string? hostId)
|
||||
{
|
||||
return Index.Replace("{hostId}", hostId);
|
||||
}
|
||||
|
||||
public static string DetailsHref(string? hostId, string? userId)
|
||||
{
|
||||
return Details.Replace("{hostId}", hostId).Replace("{userId}", userId);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Groups
|
||||
{
|
||||
public const string Index = "management/hosts/{hostId}/groups";
|
||||
|
||||
public static string IndexHref(string? hostId)
|
||||
{
|
||||
return Index.Replace("{hostId}", hostId);
|
||||
}
|
||||
}
|
||||
|
||||
public static class StoragePools
|
||||
{
|
||||
public const string Index = "management/hosts/{hostId}/storagepools";
|
||||
public const string Details = "management/hosts/{hostId}/storagepools/{storagePoolId}";
|
||||
|
||||
public static string IndexHref(string? hostId)
|
||||
{
|
||||
return Index.Replace("{hostId}", hostId);
|
||||
}
|
||||
|
||||
public static string DetailsHref(string? hostId, string? storagePoolId)
|
||||
{
|
||||
return Details.Replace("{hostId}", hostId).Replace("{storagePoolId}", storagePoolId);
|
||||
}
|
||||
|
||||
public static class VirtualDisks
|
||||
{
|
||||
public const string Index = "management/hosts/{hostId}/storagepools/{storagePoolId}/virtualdisks";
|
||||
public const string Details = "management/hosts/{hostId}/storagepools/{storagePoolId}/virtualdisks/{virtualDiskId}";
|
||||
|
||||
public static string IndexHref(string? hostId, string? storagePoolId)
|
||||
{
|
||||
return Index.Replace("{hostId}", hostId).Replace("{storagePoolId}", storagePoolId);
|
||||
}
|
||||
|
||||
public static string DetailsHref(string? hostId, string? storagePoolId, string? virtualDiskId)
|
||||
{
|
||||
return Details.Replace("{hostId}", hostId).Replace("{storagePoolId}", storagePoolId).Replace("{virtualDiskId}", virtualDiskId);
|
||||
}
|
||||
}
|
||||
|
||||
public static class PhysicalDisks
|
||||
{
|
||||
public const string Index = "management/hosts/{hostId}/storagepools/{storagePoolId}/physicaldisks";
|
||||
public const string Details = "management/hosts/{hostId}/storagepools/{storagePoolId}/physicaldisks/{physicalDiskId}";
|
||||
|
||||
public static string IndexHref(string? hostId, string? storagePoolId)
|
||||
{
|
||||
return Index.Replace("{hostId}", hostId).Replace("{storagePoolId}", storagePoolId);
|
||||
}
|
||||
|
||||
public static string DetailsHref(string? hostId, string? storagePoolId, string? physicalDiskId)
|
||||
{
|
||||
return Details.Replace("{hostId}", hostId).Replace("{storagePoolId}", storagePoolId).Replace("{physicalDiskId}", physicalDiskId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class VirtualMaschines
|
||||
{
|
||||
public const string Index = "management/hosts/{hostId}/virtualmaschines";
|
||||
public const string Details = "management/hosts/{hostId}/virtualmaschines/{virtualMaschineId}";
|
||||
|
||||
public static string IndexHref(string? hostId)
|
||||
{
|
||||
return Index.Replace("{hostId}", hostId);
|
||||
}
|
||||
|
||||
public static string DetailsHref(string? hostId, string? virtualMaschineId)
|
||||
{
|
||||
return Details.Replace("{hostId}", hostId).Replace("{virtualMaschineId}", virtualMaschineId);
|
||||
}
|
||||
|
||||
public static class Snapshots
|
||||
{
|
||||
public const string Details = "management/hosts/{hostId}/virtualmaschines/{virtualMaschineId}/snapshots/{snapshotId}";
|
||||
|
||||
public static string DetailsHref(string? hostId, string? virtualMaschineId, string? snapshotId)
|
||||
{
|
||||
return Details.Replace("{hostId}", hostId).Replace("{virtualMaschineId}", virtualMaschineId).Replace("{snapshotId}", snapshotId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Network
|
||||
{
|
||||
public static class Interfaces
|
||||
{
|
||||
public const string Index = "management/hosts/{hostId}/interfaces";
|
||||
public const string Details = "management/hosts/{hostId}/interfaces/{interfaceId}";
|
||||
|
||||
public const string Addresses = "management/hosts/{hostId}/interfaces/{interfaceId}/addresses";
|
||||
public const string Nameservers = "management/hosts/{hostId}/interfaces/{interfaceId}/nameservers";
|
||||
public const string Gateways = "management/hosts/{hostId}/interfaces/{interfaceId}/gateways";
|
||||
public const string Routes = "management/hosts/{hostId}/interfaces/{interfaceId}/routes";
|
||||
|
||||
public static string IndexHref(string? hostId)
|
||||
{
|
||||
return Index.Replace("{hostId}", hostId);
|
||||
}
|
||||
|
||||
public static string DetailsHref(string? hostId, string? interfaceId)
|
||||
{
|
||||
return Details.Replace("{hostId}", hostId).Replace("{interfaceId}", interfaceId);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Addresses
|
||||
{
|
||||
public const string Index = "management/hosts/{hostId}/addresses";
|
||||
|
||||
public static string IndexHref(string? hostId)
|
||||
{
|
||||
return Index.Replace("{hostId}", hostId);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Gateways
|
||||
{
|
||||
public const string Index = "management/hosts/{hostId}/gateways";
|
||||
|
||||
public static string IndexHref(string? hostId)
|
||||
{
|
||||
return Index.Replace("{hostId}", hostId);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Nameservers
|
||||
{
|
||||
public const string Index = "management/hosts/{hostId}/nameservers";
|
||||
|
||||
public static string IndexHref(string? hostId)
|
||||
{
|
||||
return Index.Replace("{hostId}", hostId);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Routes
|
||||
{
|
||||
public const string Index = "management/hosts/{hostId}/routes";
|
||||
|
||||
public static string IndexHref(string? hostId)
|
||||
{
|
||||
return Index.Replace("{hostId}", hostId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Hardware
|
||||
{
|
||||
public static class Mainboard
|
||||
{
|
||||
public const string Details = "management/hosts/{hostId}/mainboard";
|
||||
|
||||
public static string DetailsHref(string? hostId)
|
||||
{
|
||||
return Details.Replace("{hostId}", hostId);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Processors
|
||||
{
|
||||
public const string Details = "management/hosts/{hostId}/processors";
|
||||
|
||||
public static string DetailsHref(string? hostId)
|
||||
{
|
||||
return Details.Replace("{hostId}", hostId);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Memory
|
||||
{
|
||||
public const string Index = "management/hosts/{hostId}/memory";
|
||||
|
||||
public static string IndexHref(string? hostId)
|
||||
{
|
||||
return Index.Replace("{hostId}", hostId);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Drives
|
||||
{
|
||||
public const string Index = "management/hosts/{hostId}/drives";
|
||||
|
||||
public static string IndexHref(string? hostId)
|
||||
{
|
||||
return Index.Replace("{hostId}", hostId);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Videocards
|
||||
{
|
||||
public const string Index = "management/hosts/{hostId}/videocards";
|
||||
|
||||
public static string IndexHref(string? hostId)
|
||||
{
|
||||
return Index.Replace("{hostId}", hostId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class HostGroups
|
||||
{
|
||||
public const string Index = "management/hostgroups";
|
||||
public const string Details = "management/hostgroups/{groupId}";
|
||||
|
||||
public static string DetailsHref(string? groupId)
|
||||
{
|
||||
return Details.Replace("{groupId}", groupId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Inventory
|
||||
{
|
||||
public static class Systems
|
||||
{
|
||||
public static class Os
|
||||
{
|
||||
public const string Index = "inventory/os";
|
||||
public const string Hosts = "inventory/os/{osName}/hosts";
|
||||
public const string Guests = "inventory/os/{osName}/guests";
|
||||
|
||||
public static string HostsHref(string? osName)
|
||||
{
|
||||
return Hosts.Replace("{osName}", osName);
|
||||
}
|
||||
|
||||
public static string GuestsHref(string? osName)
|
||||
{
|
||||
return Guests.Replace("{osName}", osName);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Updates
|
||||
{
|
||||
public const string Index = "inventory/updates";
|
||||
public const string Hosts = "inventory/updates/{updateName}/hosts";
|
||||
|
||||
public static string HostsHref(string? updateName)
|
||||
{
|
||||
return Hosts.Replace("{updateName}", updateName);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Software
|
||||
{
|
||||
public const string Index = "inventory/software";
|
||||
public const string Hosts = "inventory/software/{softwareName}/hosts";
|
||||
|
||||
public static string HostsHref(string? software)
|
||||
{
|
||||
return Hosts.Replace("{softwareName}", software);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Services
|
||||
{
|
||||
public const string Index = "inventory/services";
|
||||
public const string Hosts = "inventory/services/{serviceName}/hosts";
|
||||
|
||||
public static string HostsHref(string? serviceName)
|
||||
{
|
||||
return Hosts.Replace("{serviceName}", serviceName);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Sessions
|
||||
{
|
||||
public const string Index = "inventory/sessions";
|
||||
}
|
||||
|
||||
public static class Printers
|
||||
{
|
||||
public const string Index = "inventory/printers";
|
||||
}
|
||||
|
||||
public static class Volumes
|
||||
{
|
||||
public const string Index = "inventory/volumes";
|
||||
}
|
||||
|
||||
public static class Users
|
||||
{
|
||||
public const string Index = "inventory/users";
|
||||
public const string Hosts = "inventory/users/{userName}/hosts";
|
||||
|
||||
public static string HostsHref(string? userName)
|
||||
{
|
||||
return Hosts.Replace("{userName}", userName);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Groups
|
||||
{
|
||||
public const string Index = "inventory/groups";
|
||||
public const string Hosts = "inventory/groups/{groupName}/hosts";
|
||||
|
||||
public static string HostsHref(string? groupName)
|
||||
{
|
||||
return Hosts.Replace("{groupName}", groupName);
|
||||
}
|
||||
}
|
||||
|
||||
public static class StoragePools
|
||||
{
|
||||
public const string Index = "inventory/storagepools";
|
||||
}
|
||||
|
||||
public static class VirtualMaschines
|
||||
{
|
||||
public const string Index = "inventory/virtualmaschines";
|
||||
}
|
||||
}
|
||||
|
||||
public static class Network
|
||||
{
|
||||
public static class Interfaces
|
||||
{
|
||||
public const string Index = "inventory/interfaces";
|
||||
}
|
||||
|
||||
public static class Addresses
|
||||
{
|
||||
public const string Index = "inventory/addresses";
|
||||
public const string Hosts = "inventory/addresses/{address}/hosts";
|
||||
|
||||
public static string HostsHref(string? address)
|
||||
{
|
||||
return Hosts.Replace("{address}", address);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Nameservers
|
||||
{
|
||||
public const string Index = "inventory/nameservers";
|
||||
public const string Hosts = "inventory/nameservers/{nameserverAddress}/hosts";
|
||||
|
||||
public static string HostsHref(string? nameserverAddress)
|
||||
{
|
||||
return Hosts.Replace("{nameserverAddress}", nameserverAddress);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Gateways
|
||||
{
|
||||
public const string Index = "inventory/gateways";
|
||||
public const string Hosts = "inventory/gateways/{gatewayAddress}/hosts";
|
||||
|
||||
public static string HostsHref(string? gatewayAddress)
|
||||
{
|
||||
return Hosts.Replace("{gatewayAddress}", gatewayAddress);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Routes
|
||||
{
|
||||
public const string Index = "inventory/routes";
|
||||
public const string Hosts = "inventory/routes/{routeAddress}/hosts";
|
||||
|
||||
public static string HostsHref(string? routeAddress)
|
||||
{
|
||||
return Hosts.Replace("{routeAddress}", routeAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Hardware
|
||||
{
|
||||
public static class Mainboards
|
||||
{
|
||||
public const string Index = "inventory/mainboards";
|
||||
public const string Hosts = "inventory/mainboards/{mainboardName}/hosts";
|
||||
|
||||
public static string HostsHref(string? mainboardName)
|
||||
{
|
||||
return Hosts.Replace("{mainboardName}", mainboardName);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Processors
|
||||
{
|
||||
public const string Index = "inventory/processors";
|
||||
public const string Hosts = "inventory/processors/{processorName}/hosts";
|
||||
|
||||
public static string HostsHref(string? processorName)
|
||||
{
|
||||
return Hosts.Replace("{processorName}", processorName);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Memory
|
||||
{
|
||||
public const string Index = "inventory/memory";
|
||||
public const string Hosts = "inventory/memory/{memoryName}/hosts";
|
||||
|
||||
public static string HostsHref(string? memoryName)
|
||||
{
|
||||
return Hosts.Replace("{memoryName}", memoryName);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Drives
|
||||
{
|
||||
public const string Index = "inventory/drives";
|
||||
public const string Hosts = "inventory/drives/{driveName}/hosts";
|
||||
|
||||
public static string HostsHref(string? driveName)
|
||||
{
|
||||
return Hosts.Replace("{driveName}", driveName);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Videocards
|
||||
{
|
||||
public const string Index = "inventory/videocards";
|
||||
public const string Hosts = "inventory/videocards/{videocardName}/hosts";
|
||||
|
||||
public static string HostsHref(string? videocardName)
|
||||
{
|
||||
return Hosts.Replace("{videocardName}", videocardName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Communication
|
||||
{
|
||||
public const string Index = "communication";
|
||||
|
||||
public static class Chat
|
||||
{
|
||||
public const string Index = "communication/chat";
|
||||
}
|
||||
}
|
||||
}
|
||||
39
src/Web/Insight.Web/Constants/Notification.cs
Normal file
39
src/Web/Insight.Web/Constants/Notification.cs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
using MudBlazor;
|
||||
|
||||
namespace Insight.Web.Constants;
|
||||
|
||||
public static class Notification
|
||||
{
|
||||
public static class Default
|
||||
{
|
||||
public static void Options(SnackbarOptions options)
|
||||
{
|
||||
options.HideIcon = true;
|
||||
options.ShowCloseIcon = false;
|
||||
options.RequireInteraction = false;
|
||||
options.ShowTransitionDuration = 0;
|
||||
options.HideTransitionDuration = 1000;
|
||||
options.VisibleStateDuration = 3000;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Information(ISnackbar snackbar, string message, Action<SnackbarOptions>? options = null, string key = "")
|
||||
{
|
||||
snackbar.Add(message, Severity.Info, options ?? Default.Options, key);
|
||||
}
|
||||
|
||||
public static void Success(ISnackbar snackbar, string? message = null, Action<SnackbarOptions>? options = null, string key = "")
|
||||
{
|
||||
snackbar.Add(message ?? "Success", Severity.Success, options ?? Default.Options, key);
|
||||
}
|
||||
|
||||
public static void Warning(ISnackbar snackbar, string message, Action<SnackbarOptions>? options = null, string key = "")
|
||||
{
|
||||
snackbar.Add(message, Severity.Warning, options ?? Default.Options, key);
|
||||
}
|
||||
|
||||
public static void Error(ISnackbar snackbar, string? message = null, Action<SnackbarOptions>? options = null, string key = "")
|
||||
{
|
||||
snackbar.Add(message ?? "Error", Severity.Error, options ?? Default.Options, key);
|
||||
}
|
||||
}
|
||||
186
src/Web/Insight.Web/Constants/Themes.cs
Normal file
186
src/Web/Insight.Web/Constants/Themes.cs
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
using MudBlazor;
|
||||
|
||||
namespace Insight.Web.Constants;
|
||||
|
||||
public static class Themes
|
||||
{
|
||||
public static MudTheme Default()
|
||||
{
|
||||
var theme = new MudTheme();
|
||||
|
||||
//theme.LayoutProperties.DrawerWidthRight = "300";
|
||||
|
||||
theme.Palette.Background = "#fafafa";
|
||||
//theme.Palette.Surface = "#fafafa";
|
||||
//theme.Palette.Surface = "#f6f6f6ff";
|
||||
|
||||
theme.PaletteDark.AppbarBackground = "#1f1f27";
|
||||
theme.PaletteDark.DrawerBackground = "#24242d";
|
||||
//theme.PaletteDark.DrawerBackground = "#32333dff";
|
||||
theme.PaletteDark.DrawerText = "#ffffffb2";
|
||||
theme.PaletteDark.Divider = "#ffffffb2";
|
||||
theme.PaletteDark.DividerLight = "#ffffffb2";
|
||||
//theme.PaletteDark.Surface = "#32333dff";
|
||||
theme.PaletteDark.Primary = "#ffffff";
|
||||
//theme.PaletteDark.TextPrimary = "#ffffff";
|
||||
|
||||
//Style = "background-color: #363740;"
|
||||
|
||||
return theme;
|
||||
}
|
||||
|
||||
public static MudTheme ApplicationTheme() => new()
|
||||
{
|
||||
Palette = new PaletteLight
|
||||
{
|
||||
Primary = "#283593",
|
||||
Black = "#0A0E19",
|
||||
Success = "#64A70B",
|
||||
Secondary = "#ff4081ff",
|
||||
AppbarBackground = "rgba(255,255,255,0.8)",
|
||||
AppbarText = "#424242",
|
||||
BackgroundGrey = "#F9FAFC",
|
||||
TextSecondary = "#425466",
|
||||
Dark = "#110E2D",
|
||||
DarkLighten = "#1A1643",
|
||||
GrayDefault = "#4B5563",
|
||||
GrayLight = "#9CA3AF",
|
||||
GrayLighter = "#adbdccff"
|
||||
},
|
||||
PaletteDark = new PaletteLight
|
||||
{
|
||||
Primary = "#1A237E",
|
||||
Black = "#27272f",
|
||||
Background = "rgb(21,27,34)",
|
||||
BackgroundGrey = "#27272f",
|
||||
Surface = "#212B36",
|
||||
DrawerBackground = "rgb(21,27,34)",
|
||||
DrawerText = "rgba(255,255,255, 0.50)",
|
||||
DrawerIcon = "rgba(255,255,255, 0.50)",
|
||||
AppbarBackground = "rgba(21,27,34,0.7)",
|
||||
AppbarText = "rgba(255,255,255, 0.70)",
|
||||
TextPrimary = "rgba(255,255,255, 0.70)",
|
||||
TextSecondary = "rgba(255,255,255, 0.50)",
|
||||
ActionDefault = "#adadb1",
|
||||
ActionDisabled = "rgba(255,255,255, 0.26)",
|
||||
ActionDisabledBackground = "rgba(255,255,255, 0.12)",
|
||||
DarkDarken = "rgba(21,27,34,0.7)",
|
||||
Divider = "rgba(255,255,255, 0.12)",
|
||||
DividerLight = "rgba(255,255,255, 0.06)",
|
||||
TableLines = "rgba(255,255,255, 0.12)",
|
||||
LinesDefault = "rgba(255,255,255, 0.12)",
|
||||
LinesInputs = "rgba(255,255,255, 0.3)",
|
||||
TextDisabled = "rgba(255,255,255, 0.2)"
|
||||
},
|
||||
LayoutProperties = new LayoutProperties
|
||||
{
|
||||
AppbarHeight = "80px",
|
||||
DefaultBorderRadius = "6px",
|
||||
},
|
||||
Typography = new Typography
|
||||
{
|
||||
Default = new Default
|
||||
{
|
||||
FontSize = ".8125rem",
|
||||
FontWeight = 400,
|
||||
LineHeight = 1.43,
|
||||
LetterSpacing = "normal",
|
||||
FontFamily = new string[] { "Public Sans", "Roboto", "Arial", "sans-serif" }
|
||||
},
|
||||
H1 = new H1
|
||||
{
|
||||
FontSize = "4rem",
|
||||
FontWeight = 700,
|
||||
LineHeight = 1.167,
|
||||
LetterSpacing = "-.01562em"
|
||||
},
|
||||
H2 = new H2
|
||||
{
|
||||
FontSize = "3.75rem",
|
||||
FontWeight = 300,
|
||||
LineHeight = 1.2,
|
||||
LetterSpacing = "-.00833em"
|
||||
},
|
||||
H3 = new H3
|
||||
{
|
||||
FontSize = "3rem",
|
||||
FontWeight = 600,
|
||||
LineHeight = 1.167,
|
||||
LetterSpacing = "0"
|
||||
},
|
||||
H4 = new H4
|
||||
{
|
||||
FontSize = "1.8rem",
|
||||
FontWeight = 400,
|
||||
LineHeight = 1.235,
|
||||
LetterSpacing = ".00735em"
|
||||
},
|
||||
H5 = new H5
|
||||
{
|
||||
FontSize = "1.5rem",
|
||||
FontWeight = 400,
|
||||
LineHeight = 1.334,
|
||||
LetterSpacing = "0"
|
||||
},
|
||||
H6 = new H6
|
||||
{
|
||||
FontSize = "1.125rem",
|
||||
FontWeight = 600,
|
||||
LineHeight = 1.6,
|
||||
LetterSpacing = ".0075em"
|
||||
},
|
||||
Button = new Button
|
||||
{
|
||||
FontSize = ".8125rem",
|
||||
FontWeight = 500,
|
||||
LineHeight = 1.75,
|
||||
LetterSpacing = ".02857em",
|
||||
TextTransform = "uppercase"
|
||||
|
||||
|
||||
},
|
||||
Subtitle1 = new Subtitle1
|
||||
{
|
||||
FontSize = "1rem",
|
||||
FontWeight = 400,
|
||||
LineHeight = 1.75,
|
||||
LetterSpacing = ".00938em"
|
||||
},
|
||||
Subtitle2 = new Subtitle2
|
||||
{
|
||||
FontSize = ".875rem",
|
||||
FontWeight = 500,
|
||||
LineHeight = 1.57,
|
||||
LetterSpacing = ".00714em"
|
||||
},
|
||||
Body1 = new Body1
|
||||
{
|
||||
FontSize = "0.875rem",
|
||||
FontWeight = 400,
|
||||
LineHeight = 1.5,
|
||||
LetterSpacing = ".00938em"
|
||||
},
|
||||
Body2 = new Body2
|
||||
{
|
||||
FontSize = ".8125rem",
|
||||
FontWeight = 400,
|
||||
LineHeight = 1.43,
|
||||
LetterSpacing = ".01071em"
|
||||
},
|
||||
Caption = new Caption
|
||||
{
|
||||
FontSize = ".75rem",
|
||||
FontWeight = 400,
|
||||
LineHeight = 1.66,
|
||||
LetterSpacing = ".03333em"
|
||||
},
|
||||
Overline = new Overline
|
||||
{
|
||||
FontSize = ".75rem",
|
||||
FontWeight = 400,
|
||||
LineHeight = 2.66,
|
||||
LetterSpacing = ".08333em"
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using System.Web;
|
||||
|
||||
namespace Insight.Web.Extensions;
|
||||
|
||||
public static class NavigationManagerExtensions
|
||||
{
|
||||
public static Dictionary<string, StringValues> GetQueryString(this NavigationManager navigationManager)
|
||||
{
|
||||
var uri = navigationManager.ToAbsoluteUri(navigationManager.Uri);
|
||||
if (uri is null) return new Dictionary<string, StringValues>();
|
||||
|
||||
return QueryHelpers.ParseQuery(uri.Query);
|
||||
}
|
||||
|
||||
public static void ChangeQueryStringValue(this NavigationManager navigationManager, string key, string? value = null)
|
||||
{
|
||||
var queryString = GetQueryString(navigationManager);
|
||||
|
||||
if (queryString.ContainsKey(key))
|
||||
{
|
||||
if (queryString[key] == value) return;
|
||||
queryString[key] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (value is null) return;
|
||||
queryString.Add(key, value);
|
||||
}
|
||||
|
||||
var uri = ReplaceQueryString(navigationManager.Uri, queryString);
|
||||
navigationManager.NavigateTo(uri);
|
||||
}
|
||||
|
||||
private static void ChangeQueryString(this NavigationManager navigationManager, Dictionary<string, StringValues> queryStrings)
|
||||
{
|
||||
var uri = ReplaceQueryString(navigationManager.Uri, queryStrings);
|
||||
navigationManager.NavigateTo(uri);
|
||||
}
|
||||
|
||||
private static string ReplaceQueryString(string uri, Dictionary<string, StringValues> queryString)
|
||||
{
|
||||
string url = uri;
|
||||
|
||||
foreach (var key in queryString.Keys)
|
||||
{
|
||||
url = RemoveQueryStringByKey(url, key);
|
||||
}
|
||||
|
||||
var query = new Dictionary<string, string>();
|
||||
foreach (var keys in queryString.Where(x => string.IsNullOrWhiteSpace(x.Value) is false))
|
||||
{
|
||||
query.Add(keys.Key, keys.Value.ToString());
|
||||
}
|
||||
|
||||
return QueryHelpers.AddQueryString(url, query);
|
||||
}
|
||||
|
||||
public static string ReplaceQueryStrings(string uri, Dictionary<string, string?> parameters)
|
||||
{
|
||||
string url = uri;
|
||||
|
||||
foreach (var key in parameters.Keys)
|
||||
{
|
||||
url = RemoveQueryStringByKey(url, key);
|
||||
}
|
||||
|
||||
return QueryHelpers.AddQueryString(url, parameters);
|
||||
}
|
||||
|
||||
private static string RemoveQueryStringByKey(string url, string key)
|
||||
{
|
||||
var uri = new Uri(url);
|
||||
|
||||
// this gets all the query string key value pairs as a collection
|
||||
var newQueryString = HttpUtility.ParseQueryString(uri.Query);
|
||||
|
||||
// this removes the key if exists
|
||||
newQueryString.Remove(key);
|
||||
|
||||
// this gets the page path from root without QueryString
|
||||
string pagePathWithoutQueryString = uri.GetLeftPart(UriPartial.Path);
|
||||
|
||||
return newQueryString.Count > 0
|
||||
? string.Format("{0}?{1}", pagePathWithoutQueryString, newQueryString)
|
||||
: pagePathWithoutQueryString;
|
||||
}
|
||||
}
|
||||
45
src/Web/Insight.Web/Extensions/ServiceExtensions.cs
Normal file
45
src/Web/Insight.Web/Extensions/ServiceExtensions.cs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
using Blazored.LocalStorage;
|
||||
using Blazored.SessionStorage;
|
||||
using Insight.Web.Services;
|
||||
using Microsoft.AspNetCore.Components.Server.Circuits;
|
||||
using MudBlazor.Services;
|
||||
|
||||
namespace Insight.Web.Hosting;
|
||||
|
||||
public static class ServiceExtensions
|
||||
{
|
||||
internal static IServiceCollection AddWebServices(this IServiceCollection services)
|
||||
{
|
||||
// HOSTS
|
||||
services.AddHostedService<ServiceHost>();
|
||||
|
||||
// LOCAL STORAGE
|
||||
services.AddBlazoredLocalStorage();
|
||||
services.AddBlazoredSessionStorage();
|
||||
|
||||
// BLAZOR
|
||||
services.AddHttpContextAccessor();
|
||||
services.AddRazorPages();
|
||||
services.AddServerSideBlazor(options =>
|
||||
{
|
||||
options.DetailedErrors = true;
|
||||
});
|
||||
|
||||
// CIRCUIT
|
||||
services.AddScoped<CircuitHandler>(p => p.GetRequiredService<SessionHandler>());
|
||||
services.AddScoped<SessionHandler>();
|
||||
services.AddSingleton<SessionPool>();
|
||||
|
||||
// CHAT
|
||||
services.AddSingleton<ChatService>();
|
||||
|
||||
// MUDBLAZOR
|
||||
services.AddMudServices(options =>
|
||||
{
|
||||
options.SnackbarConfiguration.PositionClass = MudBlazor.Defaults.Classes.Position.BottomLeft;
|
||||
});
|
||||
services.AddMudBlazorDialog();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
31
src/Web/Insight.Web/Extensions/StringExtensions.cs
Normal file
31
src/Web/Insight.Web/Extensions/StringExtensions.cs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
namespace Insight.Web.Extensions;
|
||||
|
||||
public static class StringExtensions
|
||||
{
|
||||
public static string? Escape(this string? value)
|
||||
{
|
||||
if (value is null) return value;
|
||||
|
||||
return value.Replace("(", @"\(").Replace(")", @"\)").Replace(@"\", @"\\");
|
||||
}
|
||||
|
||||
public static string? UriEscape(this string? value, bool revert = false)
|
||||
{
|
||||
if (value is null) return value;
|
||||
|
||||
if (revert)
|
||||
{
|
||||
value = value.Replace(@"$pc", @"%");
|
||||
value = value.Replace(@"$fs", @"/");
|
||||
value = value.Replace(@"$bs", @"\");
|
||||
}
|
||||
else
|
||||
{
|
||||
value = value.Replace(@"%", @"$pc");
|
||||
value = value.Replace(@"/", @"$fs");
|
||||
value = value.Replace(@"\", @"$bs");
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
54
src/Web/Insight.Web/Insight.Web.csproj
Normal file
54
src/Web/Insight.Web/Insight.Web.csproj
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Product>Insight</Product>
|
||||
<AssemblyName>web</AssemblyName>
|
||||
<AssemblyVersion>2023.9.18.0</AssemblyVersion>
|
||||
<RootNamespace>Insight.Web</RootNamespace>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<PublishAot>false</PublishAot>
|
||||
<PublishTrimmed>false</PublishTrimmed>
|
||||
<!--<ServerGarbageCollection>false</ServerGarbageCollection>
|
||||
<ConcurrentGarbageCollection>false</ConcurrentGarbageCollection>-->
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DebugType>none</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DebugType>none</DebugType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="7.0.1" />
|
||||
<PackageReference Include="Serilog.Extensions.Logging.File" Version="3.0.0" />
|
||||
<PackageReference Include="Blazored.LocalStorage" Version="4.4.0" />
|
||||
<PackageReference Include="Blazored.SessionStorage" Version="2.4.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="7.0.11" />
|
||||
<PackageReference Include="MudBlazor" Version="6.10.0" />
|
||||
<PackageReference Include="Vaitr.Bus" Version="0.1.3" />
|
||||
<!--Unix Serilog stuff-->
|
||||
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
|
||||
<PackageReference Include="System.Text.Encoding.Extensions" Version="4.3.0" />
|
||||
<PackageReference Include="System.Runtime.Handles" Version="4.3.0" />
|
||||
<PackageReference Include="System.IO" Version="4.3.0" />
|
||||
<PackageReference Include="System.Runtime.InteropServices" Version="4.3.0" />
|
||||
<PackageReference Include="System.Threading" Version="4.3.0" />
|
||||
<PackageReference Include="System.Threading.Tasks" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Agent\Insight.Agent.Assets\Insight.Agent.Assets.csproj" />
|
||||
<ProjectReference Include="..\..\Core\Insight.Infrastructure\Insight.Infrastructure.csproj" />
|
||||
<ProjectReference Include="..\Insight.Web.Assets\Insight.Web.Assets.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="wwwroot\media\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
295
src/Web/Insight.Web/Middleware/IdentityMiddleware.cs
Normal file
295
src/Web/Insight.Web/Middleware/IdentityMiddleware.cs
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
using Insight.Infrastructure;
|
||||
using Insight.Infrastructure.Entities;
|
||||
using Insight.Web.Constants;
|
||||
using Insight.Web.Models.Account;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using MongoDB.Driver;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Insight.Web.Middleware;
|
||||
|
||||
public class IdentityMiddleware
|
||||
{
|
||||
public static IDictionary<Guid, LoginModel> Logins { get; private set; } = new ConcurrentDictionary<Guid, LoginModel>();
|
||||
public static IDictionary<Guid, ChangePasswordModel> Passwords { get; private set; } = new ConcurrentDictionary<Guid, ChangePasswordModel>();
|
||||
|
||||
private RequestDelegate Next { get; }
|
||||
private ILogger<IdentityMiddleware> Logger { get; }
|
||||
|
||||
public IdentityMiddleware(RequestDelegate next, ILogger<IdentityMiddleware> logger)
|
||||
{
|
||||
Next = next;
|
||||
Logger = logger;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext context, IServiceProvider serviceProvider)
|
||||
{
|
||||
try
|
||||
{
|
||||
// skip blazor / internal requests
|
||||
if (context.Request.Path.Value.StartsWith("/_blazor")) return;
|
||||
if (context.Request.Path.Value.StartsWith("/_framework")) return;
|
||||
if (context.Request.Path.Value.StartsWith("/_content")) return;
|
||||
if (context.Request.Path.Value.StartsWith("/css")) return;
|
||||
if (context.Request.Path.Value.StartsWith("/fonts")) return;
|
||||
if (context.Request.Path.Value.StartsWith("/media")) return;
|
||||
if (context.Request.Path.Value.StartsWith("/js")) return;
|
||||
if (context.Request.Path.Value.StartsWith("/favicon")) return;
|
||||
|
||||
//Logger.LogWarning($"{context.Request.Path}");
|
||||
|
||||
// skip hub request (test)
|
||||
if (context.Request.Path.Value.StartsWith("/hub")) return;
|
||||
|
||||
|
||||
// ignore 2fa login request
|
||||
if (context.Request.Path.Value.StartsWith($"/{Navigation.Account.LoginTFA}")) return;
|
||||
|
||||
// ignore login request
|
||||
if (context.Request.Path.Value.StartsWith($"/{Navigation.Account.Login}")) return;
|
||||
|
||||
// 2fa signin request
|
||||
if (context.Request.Path.Value.StartsWith($"/{Navigation.Account.SignInTFA}"))
|
||||
{
|
||||
await OnSignInTFAAsync(context, serviceProvider).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// signin request
|
||||
if (context.Request.Path.Value.StartsWith($"/{Navigation.Account.SignIn}"))
|
||||
{
|
||||
await OnSignInAsync(context, serviceProvider).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// logout request
|
||||
if (context.Request.Path.Value.StartsWith($"/{Navigation.Account.Logout}"))
|
||||
{
|
||||
await OnLogoutAsync(context, serviceProvider).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
//password change request
|
||||
if (context.Request.Path.Value.StartsWith($"/{Navigation.Account.ChangePassword}"))
|
||||
{
|
||||
await OnChangePasswordAsync(context, serviceProvider).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await OnCatchRoute(context, serviceProvider).ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await Next.Invoke(context).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask OnSignInAsync(HttpContext context, IServiceProvider serviceProvider)
|
||||
{
|
||||
if (context.Request.Query.ContainsKey("key") is false) return;
|
||||
var key = Guid.Parse(context.Request.Query["key"]);
|
||||
|
||||
try
|
||||
{
|
||||
using var scope = serviceProvider.CreateScope();
|
||||
var database = scope.ServiceProvider.GetRequiredService<IMongoDatabase>();
|
||||
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<InsightUser>>();
|
||||
var signInManager = scope.ServiceProvider.GetRequiredService<SignInManager<InsightUser>>();
|
||||
|
||||
if (await userManager.FindByEmailAsync(Logins[key].Email) is not InsightUser user)
|
||||
{
|
||||
context.Response.Redirect($"/{Navigation.Account.LoginHref(Logins[key].Redirect)}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (await userManager.CheckPasswordAsync(user, Logins[key].Password) is false)
|
||||
{
|
||||
context.Response.Redirect($"/{Navigation.Account.LoginHref(Logins[key].Redirect)}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (await userManager.GetTwoFactorEnabledAsync(user))
|
||||
{
|
||||
var result = await signInManager.PasswordSignInAsync(user.UserName, Logins[key].Password, Logins[key].RememberMe, lockoutOnFailure: false).ConfigureAwait(false);
|
||||
context.Response.Redirect($"/{Navigation.Account.LoginTFAHref(key)}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Logins[key].Redirect) is false)
|
||||
{
|
||||
context.Response.Redirect($"/{Logins[key].Redirect}");
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Response.Redirect($"/{Navigation.Home}");
|
||||
}
|
||||
|
||||
await signInManager.SignInAsync(user, new AuthenticationProperties
|
||||
{
|
||||
AllowRefresh = true,
|
||||
IsPersistent = Logins[key].RememberMe,
|
||||
IssuedUtc = DateTimeOffset.UtcNow,
|
||||
ExpiresUtc = DateTimeOffset.UtcNow.AddHours(8)
|
||||
}).ConfigureAwait(false);
|
||||
|
||||
await database.UserLog()
|
||||
.InsertOneAsync(new InsightUserLogEntity
|
||||
{
|
||||
Insert = DateTime.Now,
|
||||
User = user.Id.ToString(),
|
||||
Timestamp = DateTime.Now,
|
||||
Message = $"Login ({context.Request.Host})",
|
||||
}, cancellationToken: default)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
Logins.Remove(key);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex.ToString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
//Logger.LogInformation("redirect {0}", string.Concat(context.Response.Headers));
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask OnSignInTFAAsync(HttpContext context, IServiceProvider serviceProvider)
|
||||
{
|
||||
Logger.LogInformation($"OnSignInTFAAsync ({context.Request.Path})");
|
||||
|
||||
if (context.Request.Query.ContainsKey("key") is false) return;
|
||||
|
||||
var key = Guid.Parse(context.Request.Query["key"]);
|
||||
|
||||
try
|
||||
{
|
||||
using var scope = serviceProvider.CreateScope();
|
||||
var database = scope.ServiceProvider.GetRequiredService<IMongoDatabase>();
|
||||
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<InsightUser>>();
|
||||
var signInManager = scope.ServiceProvider.GetRequiredService<SignInManager<InsightUser>>();
|
||||
|
||||
var user = await userManager.FindByEmailAsync(Logins[key].Email).ConfigureAwait(false);
|
||||
|
||||
var authenticatorCode = Logins[key].TwoFactorToken.Replace(" ", string.Empty).Replace("-", string.Empty);
|
||||
|
||||
var valid = await userManager.VerifyTwoFactorTokenAsync(user, userManager.Options.Tokens.AuthenticatorTokenProvider, authenticatorCode).ConfigureAwait(false);
|
||||
if (valid is false)
|
||||
{
|
||||
context.Response.Redirect($"/{Navigation.Account.LoginTFAHref(key)}");
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await signInManager.TwoFactorAuthenticatorSignInAsync(authenticatorCode, true, Logins[key].RememberMe).ConfigureAwait(false);
|
||||
|
||||
if (result.IsNotAllowed) return;
|
||||
if (result.IsLockedOut) return;
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Logins[key].Redirect) is false)
|
||||
{
|
||||
context.Response.Redirect($"/{Logins[key].Redirect}");
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Response.Redirect($"/{Navigation.Home}");
|
||||
}
|
||||
|
||||
await database.UserLog()
|
||||
.InsertOneAsync(new InsightUserLogEntity
|
||||
{
|
||||
Insert = DateTime.Now,
|
||||
User = user.Id.ToString(),
|
||||
Timestamp = DateTime.Now,
|
||||
Message = $"Login 2FA ({context.Request.Host})",
|
||||
}, cancellationToken: default)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Response.Redirect($"/{Navigation.Account.LoginHref(Logins[key].Redirect)}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex.ToString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
Logins.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask OnLogoutAsync(HttpContext context, IServiceProvider serviceProvider)
|
||||
{
|
||||
Logger.LogInformation($"OnLogoutAsync ({context.Request.Path})");
|
||||
|
||||
using var scope = serviceProvider.CreateScope();
|
||||
var signInManager = scope.ServiceProvider.GetRequiredService<SignInManager<InsightUser>>();
|
||||
await signInManager.SignOutAsync().ConfigureAwait(false);
|
||||
|
||||
context.Response.Redirect($"/");
|
||||
}
|
||||
|
||||
private async ValueTask OnDisconnectAsync(HttpContext context, IServiceProvider serviceProvider)
|
||||
{
|
||||
Logger.LogInformation($"OnDisconnectAsync ({context.Request.Path})");
|
||||
|
||||
using var scope = serviceProvider.CreateScope();
|
||||
var signInManager = scope.ServiceProvider.GetRequiredService<SignInManager<InsightUser>>();
|
||||
await signInManager.SignOutAsync().ConfigureAwait(false);
|
||||
|
||||
context.Abort();
|
||||
}
|
||||
|
||||
private async ValueTask OnChangePasswordAsync(HttpContext context, IServiceProvider serviceProvider)
|
||||
{
|
||||
Logger.LogInformation($"OnChangePasswordAsync ({context.Request.Path})");
|
||||
|
||||
if (context.Request.Query.ContainsKey("key") is false) return;
|
||||
|
||||
var key = Guid.Parse(context.Request.Query["key"]);
|
||||
|
||||
try
|
||||
{
|
||||
using var scope = serviceProvider.CreateScope();
|
||||
var userManager = scope.ServiceProvider.GetRequiredService<UserManager<InsightUser>>();
|
||||
|
||||
var user = await userManager.GetUserAsync(context.User).ConfigureAwait(false);
|
||||
var result = await userManager.ChangePasswordAsync(user, Passwords[key]?.OldPassword, Passwords[key]?.NewPassword).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex.ToString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
Passwords.Remove(key);
|
||||
context.Response.Redirect($"/{Navigation.Account.Profile}");
|
||||
}
|
||||
}
|
||||
|
||||
private ValueTask OnCatchRoute(HttpContext context, IServiceProvider serviceProvider)
|
||||
{
|
||||
using var scope = serviceProvider.CreateScope();
|
||||
var signInManager = scope.ServiceProvider.GetRequiredService<SignInManager<InsightUser>>();
|
||||
|
||||
//check authentication
|
||||
var authenticated = signInManager.IsSignedIn(context.User);
|
||||
if (authenticated) return default;
|
||||
|
||||
Logger.LogCritical("non auth - redirect");
|
||||
|
||||
var returnPath = context.Request.Path.Value ?? string.Empty;
|
||||
if (returnPath.StartsWith("/"))
|
||||
{
|
||||
returnPath = returnPath.Substring(1);
|
||||
}
|
||||
|
||||
context.Response.Redirect($"/{Navigation.Account.LoginHref(returnPath)}");
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
18
src/Web/Insight.Web/Models/Account/ChangePasswordModel.cs
Normal file
18
src/Web/Insight.Web/Models/Account/ChangePasswordModel.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Insight.Web.Models.Account;
|
||||
|
||||
public class ChangePasswordModel
|
||||
{
|
||||
[Required]
|
||||
[DataType(DataType.Password)]
|
||||
public string? OldPassword { get; set; }
|
||||
|
||||
[Required]
|
||||
[DataType(DataType.Password)]
|
||||
public string? NewPassword { get; set; }
|
||||
|
||||
[Required]
|
||||
[DataType(DataType.Password)]
|
||||
public string? ConfirmNewPassword { get; set; }
|
||||
}
|
||||
18
src/Web/Insight.Web/Models/Account/LoginModel.cs
Normal file
18
src/Web/Insight.Web/Models/Account/LoginModel.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Insight.Web.Models.Account;
|
||||
|
||||
public class LoginModel
|
||||
{
|
||||
public Guid Key { get; } = Guid.NewGuid();
|
||||
|
||||
//[EmailAddress, Required]
|
||||
public string? Email { get; set; }
|
||||
|
||||
//[DataType(DataType.Password), Required]
|
||||
public string? Password { get; set; }
|
||||
|
||||
public string? TwoFactorToken { get; set; }
|
||||
public bool RememberMe { get; set; }
|
||||
public string? Redirect { get; set; }
|
||||
}
|
||||
12
src/Web/Insight.Web/Models/ChatMessage.cs
Normal file
12
src/Web/Insight.Web/Models/ChatMessage.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
using MongoDB.Bson;
|
||||
|
||||
namespace Insight.Web.Models;
|
||||
|
||||
public class ChatMessage
|
||||
{
|
||||
public ObjectId Id { get; } = ObjectId.GenerateNewId();
|
||||
public DateTime CreatedDate { get; } = DateTime.Now;
|
||||
|
||||
public ObjectId? SenderId { get; set; }
|
||||
public string? Message { get; set; }
|
||||
}
|
||||
32
src/Web/Insight.Web/Models/ChatSession.cs
Normal file
32
src/Web/Insight.Web/Models/ChatSession.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
using MongoDB.Bson;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Insight.Web.Models;
|
||||
|
||||
public class ChatSession
|
||||
{
|
||||
public ObjectId Id { get; } = ObjectId.GenerateNewId();
|
||||
public IEnumerable<ChatUser> Members { get; }
|
||||
public ConcurrentBag<ChatMessage> Messages { get; } = new();
|
||||
|
||||
private readonly Func<ChatSession, ChatMessage, CancellationToken, Task> OnMessageSent;
|
||||
|
||||
public ChatSession(IEnumerable<ChatUser> members, Func<ChatSession, ChatMessage, CancellationToken, Task> onMessageSent)
|
||||
{
|
||||
Members = members;
|
||||
OnMessageSent = onMessageSent;
|
||||
}
|
||||
|
||||
public async Task SendMessage(ChatUser sender, string message, CancellationToken cancellationToken)
|
||||
{
|
||||
var cm = new ChatMessage
|
||||
{
|
||||
SenderId = sender.Uid,
|
||||
Message = message
|
||||
};
|
||||
|
||||
Messages.Add(cm);
|
||||
|
||||
await OnMessageSent(this, cm, cancellationToken);
|
||||
}
|
||||
}
|
||||
30
src/Web/Insight.Web/Models/ChatUser.cs
Normal file
30
src/Web/Insight.Web/Models/ChatUser.cs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
using MongoDB.Bson;
|
||||
|
||||
namespace Insight.Web.Models;
|
||||
|
||||
public class ChatUser
|
||||
{
|
||||
public ObjectId Uid { get; set; }
|
||||
public bool Online { get; set; }
|
||||
public string? Username { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public byte[]? Avatar { get; set; }
|
||||
|
||||
public ChatUser(ObjectId uid)
|
||||
{
|
||||
Uid = uid;
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj == null || GetType() != obj.GetType()) return false;
|
||||
if (obj is ChatUser user && Uid != user.Uid) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Uid.GetHashCode();
|
||||
}
|
||||
}
|
||||
9
src/Web/Insight.Web/Models/LocalStorage.cs
Normal file
9
src/Web/Insight.Web/Models/LocalStorage.cs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Insight.Web.Models;
|
||||
|
||||
public record LocalStorage
|
||||
{
|
||||
[JsonPropertyName("darkmode")]
|
||||
public bool DarkMode { get; init; }
|
||||
}
|
||||
13
src/Web/Insight.Web/Models/SessionState.cs
Normal file
13
src/Web/Insight.Web/Models/SessionState.cs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
using MongoDB.Bson;
|
||||
|
||||
namespace Insight.Web.Models;
|
||||
|
||||
public class SessionState
|
||||
{
|
||||
public string? Id { get; set; }
|
||||
public ObjectId? Uid { get; set; }
|
||||
public bool Connected { get; set; }
|
||||
public bool Authenticated { get; set; }
|
||||
public string? Username { get; set; }
|
||||
public string? Page { get; set; }
|
||||
}
|
||||
18
src/Web/Insight.Web/Models/SessionStorage.cs
Normal file
18
src/Web/Insight.Web/Models/SessionStorage.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Insight.Web.Models;
|
||||
|
||||
public record SessionStorage
|
||||
{
|
||||
[JsonPropertyName("drawer")]
|
||||
public bool Drawer { get; init; } = true;
|
||||
|
||||
[JsonPropertyName("mainmenu")]
|
||||
public MainMenu MainMenu { get; init; } = new();
|
||||
}
|
||||
|
||||
public record MainMenu
|
||||
{
|
||||
[JsonPropertyName("management")]
|
||||
public bool Management { get; init; }
|
||||
}
|
||||
24
src/Web/Insight.Web/Network/Handlers/ConsoleHandler.cs
Normal file
24
src/Web/Insight.Web/Network/Handlers/ConsoleHandler.cs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
using Insight.Web.Interfaces;
|
||||
using Insight.Web.Messages;
|
||||
using Vaitr.Bus;
|
||||
|
||||
namespace Insight.Web.Network.Handlers
|
||||
{
|
||||
public class ConsoleHandler : IWebMessageHandler<WebSession>
|
||||
{
|
||||
private readonly Bus _bus;
|
||||
|
||||
public ConsoleHandler(Bus bus)
|
||||
{
|
||||
_bus = bus;
|
||||
}
|
||||
|
||||
public async ValueTask HandleAsync<TMessage>(WebSession sender, TMessage message, CancellationToken cancellationToken) where TMessage : IWebMessage
|
||||
{
|
||||
if (message is ConsoleQueryProxy consoleQuery)
|
||||
{
|
||||
await _bus.PublishAsync(consoleQuery, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
56
src/Web/Insight.Web/Network/WebSession.cs
Normal file
56
src/Web/Insight.Web/Network/WebSession.cs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
using Insight.Web.Interfaces;
|
||||
using Insight.Web.Messages;
|
||||
using Vaitr.Network;
|
||||
|
||||
namespace Insight.Web.Network
|
||||
{
|
||||
public class WebSession : TcpSession<IWebMessage>
|
||||
{
|
||||
private readonly IEnumerable<IWebMessageHandler<WebSession>> _handlers;
|
||||
|
||||
public WebSession(IEnumerable<IWebMessageHandler<WebSession>> handlers, ISerializer<IWebMessage> serializer, ILogger<WebSession> logger) : base(serializer, logger)
|
||||
{
|
||||
_handlers = handlers;
|
||||
}
|
||||
|
||||
protected override ValueTask OnConnectedAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogInformation("Web ({ep?}) connected", RemoteEndPoint);
|
||||
return default;
|
||||
}
|
||||
|
||||
protected override ValueTask OnDisconnectedAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogInformation("Web ({ep?}) disconnected", RemoteEndPoint);
|
||||
return default;
|
||||
}
|
||||
|
||||
protected override ValueTask OnSentAsync(IPacketContext<IWebMessage> context, CancellationToken cancellationToken)
|
||||
{
|
||||
return base.OnSentAsync(context, cancellationToken);
|
||||
}
|
||||
|
||||
protected override async ValueTask OnReceivedAsync(IPacketContext<IWebMessage> context, CancellationToken cancellationToken)
|
||||
{
|
||||
await base.OnReceivedAsync(context, cancellationToken);
|
||||
|
||||
foreach (var handler in _handlers)
|
||||
{
|
||||
try
|
||||
{
|
||||
await handler.HandleAsync(this, context.Packet, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning("Web ({ep?}) {ex}", RemoteEndPoint, ex.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override ValueTask OnHeartbeatAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_logger.LogInformation("Web ({ep?}) Heartbeat", RemoteEndPoint);
|
||||
return default;
|
||||
}
|
||||
}
|
||||
}
|
||||
130
src/Web/Insight.Web/Pages/Account/Login.razor
Normal file
130
src/Web/Insight.Web/Pages/Account/Login.razor
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
@using Insight.Web.Models.Account;
|
||||
|
||||
@inherits ComponentBase
|
||||
@layout LoginLayout
|
||||
|
||||
@inject IJSRuntime JSRuntime
|
||||
|
||||
<PageTitle>@_title</PageTitle>
|
||||
|
||||
<style>
|
||||
input:-webkit-autofill,
|
||||
input:-webkit-autofill:hover,
|
||||
input:-webkit-autofill:focus,
|
||||
input:-webkit-autofill:active {
|
||||
-webkit-box-shadow: 0 0 0px 1000px white inset !important;
|
||||
}
|
||||
.mud-input{
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.blur-layer {
|
||||
position: absolute;
|
||||
backdrop-filter: blur(10px);
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.app-caption {
|
||||
font-weight: bold;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
-moz-user-select: none; /* Firefox */
|
||||
-webkit-user-select: none; /* Chrome and Safari */
|
||||
-ms-user-select: none; /* IE 10+ and Edge */
|
||||
}
|
||||
|
||||
.app-caption {
|
||||
font-weight: bold;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
-moz-user-select: none; /* Firefox */
|
||||
-webkit-user-select: none; /* Chrome and Safari */
|
||||
-ms-user-select: none; /* IE 10+ and Edge */
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
||||
<div id="particles" style="width: 100%; height: calc(100vh - 6px); position: absolute;" />
|
||||
|
||||
<MudGrid Justify="Justify.Center" Spacing="0" Style="height: 100vh; align-items: center;">
|
||||
<MudItem xs="12" md="6" lg="5" xl="4" xxl="3" Style="height: 100%;">
|
||||
<div style="position: relative; height: 100%;">
|
||||
<div class="blur-layer" style="height: 100%; width: 100%; z-index: 2;"></div>
|
||||
<div style="display: flex; align-items: center; justify-content: center; height: 100%;">
|
||||
<div style="width: 80%; z-index: 3;">
|
||||
<div class="d-md-none d-block">
|
||||
<p class="app-caption" style="font-size: 30px; color: black;">
|
||||
INSIGHT
|
||||
</p>
|
||||
</div>
|
||||
@if (Key is null)
|
||||
{
|
||||
<EditForm Model="@_model" OnValidSubmit="()=>SubmitAsync(_model)">
|
||||
<MudGrid>
|
||||
<MudItem xs="12" md="12" lg="12">
|
||||
<MudTextField T="string" @bind-Value="_model.Email" For="()=>_model.Email" Placeholder="E-Mail" Variant="Variant.Outlined" />
|
||||
</MudItem>
|
||||
<MudItem xs="12" md="12" lg="12">
|
||||
<MudTextField @bind-Value="_model.Password" For="()=>_model.Password" InputType="@(_passwordVisible ? InputType.Text : InputType.Password)"
|
||||
Placeholder="Password" Variant="Variant.Outlined" Adornment="Adornment.End" AdornmentIcon="@(_passwordVisible ? Icons.Material.Filled.VisibilityOff : Icons.Material.Filled.Visibility)"
|
||||
OnAdornmentClick="()=>_passwordVisible = !_passwordVisible" />
|
||||
</MudItem>
|
||||
<MudItem xs="12" md="12" lg="12" Class="d-flex justify-space-between">
|
||||
<MudSpacer />
|
||||
<MudCheckBox T="bool" Label="Remember" @bind-Value="_model.RememberMe" Color="Color.Primary" Class="ml-n1" />
|
||||
</MudItem>
|
||||
<MudItem xs="12" md="12" lg="12" Class="d-flex justify-center">
|
||||
<MudButton ButtonType="ButtonType.Submit" Variant="Variant.Outlined" DisableElevation Color="Color.Surface" Size="Size.Large" Style="width: 100%; background-color: white;">
|
||||
Login
|
||||
</MudButton>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
</EditForm>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudGrid>
|
||||
<MudItem xs="12" md="12" lg="12">
|
||||
<MudTextField T="string" @bind-Value="_code" Placeholder="Authenticator Token" Variant="Variant.Outlined" />
|
||||
</MudItem>
|
||||
<MudItem xs="12" md="12" lg="12" Class="d-flex justify-center">
|
||||
<MudButton ButtonType="ButtonType.Submit" Variant="Variant.Outlined" DisableElevation Color="Color.Surface" Size="Size.Large" Style="width: 100%; background-color: white;" OnClick="()=>SubmitTwoFactor(_code)">
|
||||
Authenticate
|
||||
</MudButton>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</MudItem>
|
||||
<MudItem xs="12" md="6" lg="7" xl="8" xxl="9" Class="d-none d-md-block" Style="background-color: black; width:100%; height:100%;">
|
||||
<div style="display: flex; align-items: center; justify-content: center; height: 100%;">
|
||||
<div style="z-index: 3;">
|
||||
<p class="app-caption font-size-responsive" style="color: white;">
|
||||
INSIGHT
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
|
||||
@code{
|
||||
private LoginModel _model = new();
|
||||
private string? _code;
|
||||
private bool _passwordVisible;
|
||||
|
||||
private async Task InitializeParticlesAsync()
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("LoadParticles", "particles");
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
await InitializeParticlesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
76
src/Web/Insight.Web/Pages/Account/Login.razor.cs
Normal file
76
src/Web/Insight.Web/Pages/Account/Login.razor.cs
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
using Insight.Infrastructure.Entities;
|
||||
using Insight.Infrastructure.Services;
|
||||
using Insight.Web.Constants;
|
||||
using Insight.Web.Extensions;
|
||||
using Insight.Web.Middleware;
|
||||
using Insight.Web.Models.Account;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using MudBlazor;
|
||||
|
||||
namespace Insight.Web.Pages.Account;
|
||||
|
||||
[Route(Navigation.Account.Login)]
|
||||
[Route(Navigation.Account.LoginTFA)]
|
||||
public partial class Login
|
||||
{
|
||||
[Parameter] public Guid? Key { get; set; }
|
||||
|
||||
[Inject] private UserManager<InsightUser> UserManager { get; init; } = default!;
|
||||
[Inject] private IdentityService IdentityService { get; init; } = default!;
|
||||
[Inject] private NavigationManager NavigationManager { get; init; } = default!;
|
||||
[Inject] private ISnackbar Snackbar { get; init; } = default!;
|
||||
|
||||
private readonly string _title = "Login|Insight";
|
||||
|
||||
private async Task SubmitAsync(LoginModel model)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(model.Email))
|
||||
{
|
||||
Notification.Error(Snackbar, "Invalid E-Mail");
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(model.Password))
|
||||
{
|
||||
Notification.Error(Snackbar, "Invalid Password");
|
||||
return;
|
||||
}
|
||||
|
||||
var user = await IdentityService.GetByEmailAsync(model.Email).ConfigureAwait(false);
|
||||
if (user is null || user.LockoutEnabled && user.LockoutEnd > DateTime.UtcNow || await UserManager.CheckPasswordAsync(user, model.Password) is false)
|
||||
{
|
||||
Notification.Error(Snackbar, "Access denied");
|
||||
return;
|
||||
}
|
||||
|
||||
var query = NavigationManager.GetQueryString();
|
||||
if (query.TryGetValue("redirect", out var redirect))
|
||||
{
|
||||
model.Redirect = redirect;
|
||||
}
|
||||
|
||||
IdentityMiddleware.Logins[model.Key] = model;
|
||||
NavigationManager.NavigateTo(Navigation.Account.SignInHref(model.Key), true);
|
||||
}
|
||||
|
||||
private void SubmitTwoFactor(string? code)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(code))
|
||||
{
|
||||
Notification.Information(Snackbar, "Enter Authenticator Code");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
IdentityMiddleware.Logins[Key.Value].TwoFactorToken = code;
|
||||
NavigationManager.NavigateTo(Navigation.Account.SignInTFAHref(Key.Value), true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Notification.Error(Snackbar, "Invalid Security Token");
|
||||
NavigationManager.NavigateTo(Navigation.Account.LoginHref(null), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
32
src/Web/Insight.Web/Pages/Account/LoginTFA.razor
Normal file
32
src/Web/Insight.Web/Pages/Account/LoginTFA.razor
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
@inherits ComponentBase
|
||||
|
||||
|
||||
|
||||
@if (true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
<BaseContainer Title="@_title">
|
||||
<Content>
|
||||
<div class="h-100" style="display:flex; height: 90vh; margin: auto;">
|
||||
<div style="margin:auto; height: 35vh;">
|
||||
<MudGrid>
|
||||
<MudItem xs="12" md="12" lg="12">
|
||||
<MudTextField T="string" Label="Authenticator Token" Placeholder="Token" @bind-Value="_code"
|
||||
AutoFocus Variant="Variant.Outlined" />
|
||||
</MudItem>
|
||||
<MudItem xs="12" md="12" lg="12" Class="d-flex justify-center">
|
||||
<MudButton ButtonType="ButtonType.Submit" Variant="Variant.Filled" Color="Color.Surface" Size="Size.Large" Style="width: 100%;" OnClick="()=>Submit(_code)">
|
||||
Login
|
||||
</MudButton>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
</div>
|
||||
</div>
|
||||
</Content>
|
||||
</BaseContainer>
|
||||
|
||||
@code{
|
||||
private string? _code;
|
||||
}
|
||||
29
src/Web/Insight.Web/Pages/Account/LoginTFA.razor.cs
Normal file
29
src/Web/Insight.Web/Pages/Account/LoginTFA.razor.cs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
using Insight.Web.Constants;
|
||||
using Insight.Web.Middleware;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MudBlazor;
|
||||
|
||||
namespace Insight.Web.Pages.Account;
|
||||
|
||||
//[Route(Navigation.Account.LoginTFA)]
|
||||
public partial class LoginTFA
|
||||
{
|
||||
[Parameter] public Guid Key { get; set; }
|
||||
|
||||
[Inject] private NavigationManager NavigationManager { get; init; } = default!;
|
||||
[Inject] private ISnackbar Snackbar { get; init; } = default!;
|
||||
|
||||
private readonly string _title = "Login|Insight";
|
||||
|
||||
private void Submit(string? code)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(code))
|
||||
{
|
||||
Notification.Information(Snackbar, "Enter Authenticator Code");
|
||||
return;
|
||||
}
|
||||
|
||||
IdentityMiddleware.Logins[Key].TwoFactorToken = code;
|
||||
NavigationManager.NavigateTo(Navigation.Account.SignInTFAHref(Key), true);
|
||||
}
|
||||
}
|
||||
30
src/Web/Insight.Web/Pages/Account/Profile.razor
Normal file
30
src/Web/Insight.Web/Pages/Account/Profile.razor
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
@inherits ComponentBase
|
||||
|
||||
<BaseContainer Title="@_title" Breadcrumbs="@_breadcrumbs">
|
||||
<Content>
|
||||
@if (Account is not null)
|
||||
{
|
||||
<MudGrid>
|
||||
<MudItem xs="12" sm="6" md="6" lg="3">
|
||||
<KeyValueCard T="string" Key="E-Mail" Value="@Account.Email" Icon="@Icons.Material.Outlined.Email" />
|
||||
</MudItem>
|
||||
<MudItem xs="12" sm="6" md="6" lg="3">
|
||||
<KeyValueCard T="string" Key="Password" Value="@("******")" OnClick="()=>_passwordDialog?.Toggle()" Icon="@Icons.Material.Outlined.Password" />
|
||||
</MudItem>
|
||||
<MudItem xs="12" sm="6" md="6" lg="3">
|
||||
<KeyValueCard T="string" Key="2FA" Value="@(Account.TwoFactorEnabled ? "Enabled" : "Disabled")" Icon="@Icons.Material.Outlined.SecurityUpdateGood" OnClick="()=>_twoFactorDialog?.Toggle()" />
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
|
||||
<CascadingValue Name="User" Value="@Account">
|
||||
<ProfilePasswordDialog @ref="_passwordDialog" />
|
||||
<ProfileTwoFactorDialog @ref="_twoFactorDialog" OnToggle="OnRefreshAsync" />
|
||||
</CascadingValue>
|
||||
}
|
||||
</Content>
|
||||
</BaseContainer>
|
||||
|
||||
@code{
|
||||
private ProfilePasswordDialog? _passwordDialog;
|
||||
private ProfileTwoFactorDialog? _twoFactorDialog;
|
||||
}
|
||||
40
src/Web/Insight.Web/Pages/Account/Profile.razor.cs
Normal file
40
src/Web/Insight.Web/Pages/Account/Profile.razor.cs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
using Insight.Infrastructure.Entities;
|
||||
using Insight.Infrastructure.Services;
|
||||
using Insight.Web.Constants;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using MudBlazor;
|
||||
|
||||
namespace Insight.Web.Pages.Account;
|
||||
|
||||
[Route(Navigation.Account.Profile)]
|
||||
public partial class Profile
|
||||
{
|
||||
[Parameter] public InsightUser? Account { get; set; }
|
||||
|
||||
[Inject] private IdentityService IdentityService { get; init; } = default!;
|
||||
[Inject] private AuthenticationStateProvider AuthenticationStateProvider { get; init; } = default!;
|
||||
|
||||
private readonly string _title = "Profile|Insight";
|
||||
private readonly List<BreadcrumbItem> _breadcrumbs = new()
|
||||
{
|
||||
new BreadcrumbItem("Home", href: Navigation.Home),
|
||||
new BreadcrumbItem("Account", href: Navigation.Account.Profile),
|
||||
new BreadcrumbItem("Profile", href: "#", true)
|
||||
};
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await OnRefreshAsync();
|
||||
}
|
||||
|
||||
private async Task OnRefreshAsync()
|
||||
{
|
||||
var state = await AuthenticationStateProvider.GetAuthenticationStateAsync();
|
||||
if (state?.User?.Identity?.Name is null) return;
|
||||
|
||||
Account = await IdentityService.GetByEmailAsync(state.User.Identity.Name).ConfigureAwait(false);
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
@using Insight.Web.Models.Account;
|
||||
|
||||
<MudDrawer @bind-Open="_visible" Anchor="Anchor.End" Elevation="0" Variant="@DrawerVariant.Temporary" ClipMode="DrawerClipMode.Always" Width="400px" Style="max-width:auto;">
|
||||
<MudDrawerHeader>
|
||||
<MudText Typo="Typo.h6">
|
||||
Password
|
||||
</MudText>
|
||||
</MudDrawerHeader>
|
||||
<MudStack Justify="Justify.Center" Class="px-6">
|
||||
<EditForm Model="@_model" OnValidSubmit="()=>Submit(_model)">
|
||||
<DataAnnotationsValidator />
|
||||
<MudStack Justify="Justify.Center" Spacing="5">
|
||||
<MudItem>
|
||||
<MudTextField Label="Current" Variant="Variant.Text" @bind-Value="_model.OldPassword" For="()=>_model.OldPassword"
|
||||
Margin="Margin.Dense" InputType="@(_passwordCurrentVisible ? InputType.Text : InputType.Password)"
|
||||
Adornment="Adornment.End" AdornmentIcon="@(_passwordCurrentVisible ? Icons.Material.Filled.VisibilityOff : Icons.Material.Filled.Visibility)"
|
||||
OnAdornmentClick="()=>_passwordCurrentVisible = !_passwordCurrentVisible" />
|
||||
</MudItem>
|
||||
<MudItem>
|
||||
<MudTextField Label="New" Variant="Variant.Text" @bind-Value="_model.NewPassword" For="()=>_model.NewPassword"
|
||||
Margin="Margin.Dense" InputType="@(_passwordNewVisible ? InputType.Text : InputType.Password)"
|
||||
Adornment="Adornment.End" AdornmentIcon="@(_passwordNewVisible ? Icons.Material.Filled.VisibilityOff : Icons.Material.Filled.Visibility)"
|
||||
OnAdornmentClick="()=>_passwordNewVisible = !_passwordNewVisible" />
|
||||
</MudItem>
|
||||
<MudItem>
|
||||
<MudTextField Label="Confirm New" Variant="Variant.Text" @bind-Value="_model.ConfirmNewPassword" For="()=>_model.ConfirmNewPassword"
|
||||
Margin="Margin.Dense" InputType="@(_passwordCofirmVisible ? InputType.Text : InputType.Password)"
|
||||
Adornment="Adornment.End" AdornmentIcon="@(_passwordCofirmVisible ? Icons.Material.Filled.VisibilityOff : Icons.Material.Filled.Visibility)"
|
||||
OnAdornmentClick="()=>_passwordCofirmVisible = !_passwordCofirmVisible" />
|
||||
</MudItem>
|
||||
<MudItem Class="d-flex justify-center mt-7">
|
||||
<MudButton ButtonType="ButtonType.Submit" Variant="Variant.Filled" Color="Color.Secondary" Size="Size.Large" DisableElevation Style="width: 100%;">
|
||||
Change Password
|
||||
</MudButton>
|
||||
</MudItem>
|
||||
</MudStack>
|
||||
</EditForm>
|
||||
</MudStack>
|
||||
</MudDrawer>
|
||||
|
||||
@code{
|
||||
private bool _visible;
|
||||
|
||||
private bool _passwordCurrentVisible;
|
||||
private bool _passwordNewVisible;
|
||||
private bool _passwordCofirmVisible;
|
||||
|
||||
private ChangePasswordModel _model = new();
|
||||
|
||||
public void Toggle()
|
||||
{
|
||||
_visible = !_visible;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
using Insight.Infrastructure.Entities;
|
||||
using Insight.Web.Constants;
|
||||
using Insight.Web.Middleware;
|
||||
using Insight.Web.Models.Account;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MudBlazor;
|
||||
|
||||
namespace Insight.Web.Pages.Account;
|
||||
|
||||
public partial class ProfilePasswordDialog
|
||||
{
|
||||
[CascadingParameter(Name = "User")] public InsightUser? User { get; set; }
|
||||
|
||||
[Inject] private NavigationManager NavigationManager { get; init; } = default!;
|
||||
[Inject] private ISnackbar Snackbar { get; init; } = default!;
|
||||
|
||||
private void Submit(ChangePasswordModel model)
|
||||
{
|
||||
if (model.NewPassword != model.ConfirmNewPassword)
|
||||
{
|
||||
Notification.Error(Snackbar, "Passwords not matching");
|
||||
return;
|
||||
}
|
||||
|
||||
var key = Guid.NewGuid();
|
||||
IdentityMiddleware.Passwords[key] = model;
|
||||
|
||||
NavigationManager.NavigateTo(Navigation.Account.ChangePasswordHref(key), true);
|
||||
}
|
||||
}
|
||||
221
src/Web/Insight.Web/Pages/Account/ProfileTwoFactorDialog.razor
Normal file
221
src/Web/Insight.Web/Pages/Account/ProfileTwoFactorDialog.razor
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
@using System.ComponentModel.DataAnnotations;
|
||||
<MudDrawer @bind-Open="Visible" Anchor="Anchor.End" Elevation="0" Variant="@DrawerVariant.Temporary" ClipMode="DrawerClipMode.Always" Width="400px">
|
||||
<MudDrawerHeader>
|
||||
<MudText Typo="Typo.h6">
|
||||
2FA
|
||||
</MudText>
|
||||
</MudDrawerHeader>
|
||||
|
||||
<MudStack Justify="Justify.Center" Class="px-6" Spacing="3">
|
||||
<div class="d-flex justify-center" style="height: @(!_showQrCode ? 0 : 272)px;">
|
||||
<span id="qrc" hidden="@(!_showQrCode)" style="border-style: solid; border-color: white; border-width: 1px;" />
|
||||
@*<div style="width:50px;" />
|
||||
<MudText Align="Align.Left" Typo="Typo.h6" hidden="@(!_showQrCode)" Style="text-decoration:overline;">
|
||||
@AuthenticatorFormatKey
|
||||
</MudText>*@
|
||||
</div>
|
||||
<div hidden="@(!_showQrCode)">
|
||||
@*<MudDivider />*@
|
||||
<MudText Align="Align.Center" Typo="Typo.subtitle2" Style="text-decoration:none;">
|
||||
@_authenticatorFormatKey
|
||||
</MudText>
|
||||
@*<MudDivider />*@
|
||||
</div>
|
||||
|
||||
@if (_content == Content.Options)
|
||||
{
|
||||
@if (true)
|
||||
{
|
||||
_showQrCode = false;
|
||||
}
|
||||
|
||||
@*@if (false) // test
|
||||
{
|
||||
<MudButton OnClick="()=>OnChangeAsync(Content.UseRecovery)" Variant="Variant.Outlined" DisableElevation Size="Size.Large" Color="Color.Info">
|
||||
Recovery
|
||||
</MudButton>
|
||||
}*@
|
||||
|
||||
@if (_enabled)
|
||||
{
|
||||
<MudButton OnClick="()=>OnChangeAsync(Content.Validate)" Variant="Variant.Outlined" DisableElevation Size="Size.Large" Color="Color.Info">
|
||||
Add Device
|
||||
</MudButton>
|
||||
}
|
||||
|
||||
@if (_enabled)
|
||||
{
|
||||
<MudButton OnClick="DisableAsync" Variant="Variant.Filled" DisableElevation Size="Size.Large" Color="Color.Warning">
|
||||
Disable
|
||||
</MudButton>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudButton OnClick="()=>OnChangeAsync(Content.Enable)" Variant="Variant.Filled" DisableElevation Size="Size.Large" Color="Color.Success">
|
||||
Enable
|
||||
</MudButton>
|
||||
}
|
||||
|
||||
<MudDivider Class="mt-3 mb-3" />
|
||||
|
||||
<MudButton OnClick="()=>OnChangeAsync(Content.ResetRecovery)" Variant="Variant.Outlined" DisableElevation Size="Size.Large" Color="Color.Error">
|
||||
Reset Recovery Codes
|
||||
</MudButton>
|
||||
|
||||
<MudButton OnClick="()=>OnChangeAsync(Content.Delete)" Variant="Variant.Filled" DisableElevation Size="Size.Large" Color="Color.Error">
|
||||
Delete Authenticator
|
||||
</MudButton>
|
||||
}
|
||||
else if (_content == Content.Validate)
|
||||
{
|
||||
@if (true)
|
||||
{
|
||||
_showQrCode = true;
|
||||
}
|
||||
|
||||
<EditForm Model="@_code" OnValidSubmit="()=>ValidateAsync(_code)" class="mt-3">
|
||||
<MudTextField T="string" Label="Authenticator Code" Variant="Variant.Outlined" Margin="Margin.Dense" @bind-Value="_code" Clearable AutoFocus />
|
||||
<MudStack Justify="Justify.Center" Row Class="mt-4">
|
||||
<MudButton OnClick="()=>OnChangeAsync(Content.Options)" Variant="Variant.Outlined" DisableElevation Size="Size.Large" Color="Color.Surface">
|
||||
Cancel
|
||||
</MudButton>
|
||||
<MudButton ButtonType="ButtonType.Submit" Variant="Variant.Filled" DisableElevation Size="Size.Large" Color="Color.Info" Style="width:100%;">
|
||||
Validate
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
</EditForm>
|
||||
}
|
||||
else if (_content == Content.Enable)
|
||||
{
|
||||
@if (true)
|
||||
{
|
||||
_showQrCode = true;
|
||||
}
|
||||
|
||||
<MudTextField T="string" @bind-Value="@_code" Label="Authenticator Code" Variant="Variant.Outlined" Margin="Margin.Dense" Clearable AutoFocus />
|
||||
<MudStack Justify="Justify.Center" Row Class="mt-4">
|
||||
<MudButton OnClick="()=>OnChangeAsync(Content.Options)" Variant="Variant.Outlined" DisableElevation Size="Size.Large" Color="Color.Surface">
|
||||
Cancel
|
||||
</MudButton>
|
||||
<MudButton OnClick="()=>EnableAsync(_code)" Variant="Variant.Filled" DisableElevation Size="Size.Large" Color="Color.Success" Style="width: 100%;">
|
||||
Enable
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
}
|
||||
else if (_content == Content.Delete)
|
||||
{
|
||||
@if (true)
|
||||
{
|
||||
_showQrCode = false;
|
||||
}
|
||||
|
||||
<MudStack Justify="Justify.Center" Row>
|
||||
<MudButton OnClick="()=>OnChangeAsync(Content.Options)" Variant="Variant.Outlined" DisableElevation Size="Size.Large" Color="Color.Surface">
|
||||
Cancel
|
||||
</MudButton>
|
||||
<MudButton OnClick="DeleteAsync" Variant="Variant.Filled" DisableElevation Size="Size.Large" Color="Color.Error" FullWidth>
|
||||
Confirm Delete
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
}
|
||||
else if (_content == Content.UseRecovery)
|
||||
{
|
||||
@if (true)
|
||||
{
|
||||
_showQrCode = false;
|
||||
}
|
||||
|
||||
<EditForm Model="@_code" OnValidSubmit="()=>UseRecoveryAsync(_code)">
|
||||
<MudTextField T="string" Label="Recovery Code" Variant="Variant.Outlined" Margin="Margin.Dense" @bind-Value="_code" Clearable AutoFocus />
|
||||
<MudStack Justify="Justify.Center" Row Class="mt-4">
|
||||
<MudButton OnClick="()=>OnChangeAsync(Content.Options)" Variant="Variant.Outlined" DisableElevation Size="Size.Large" Color="Color.Surface">
|
||||
Cancel
|
||||
</MudButton>
|
||||
<MudButton ButtonType="ButtonType.Submit" Variant="Variant.Filled" DisableElevation Size="Size.Large" Color="Color.Error" Style="width: 100%;">
|
||||
Recover
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
</EditForm>
|
||||
}
|
||||
else if (_content == Content.ResetRecovery)
|
||||
{
|
||||
@if (true)
|
||||
{
|
||||
_showQrCode = false;
|
||||
}
|
||||
|
||||
<MudStack Justify="Justify.Center" Row>
|
||||
<MudButton OnClick="()=>OnChangeAsync(Content.Options)" Variant="Variant.Outlined" DisableElevation Size="Size.Large" Color="Color.Surface">
|
||||
Cancel
|
||||
</MudButton>
|
||||
<MudButton OnClick="ResetRecoveryAsync" Variant="Variant.Filled" DisableElevation Size="Size.Large" Color="Color.Error" FullWidth>
|
||||
Confirm Reset
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
}
|
||||
else if (_content == Content.ShowRecovery)
|
||||
{
|
||||
@if (true)
|
||||
{
|
||||
_showQrCode = false;
|
||||
}
|
||||
|
||||
@if (_recoveryCodes != null)
|
||||
{
|
||||
@foreach (var rc in _recoveryCodes)
|
||||
{
|
||||
<MudText Align="Align.Center" Typo="Typo.h4">
|
||||
@rc
|
||||
</MudText>
|
||||
|
||||
<MudDivider Class="mt-3 mb-3" />
|
||||
}
|
||||
}
|
||||
|
||||
<MudButton OnClick="()=>OnChangeAsync(Content.Options)" Variant="Variant.Filled" DisableElevation Size="Size.Large" Color="Color.Warning">
|
||||
Acknowledged
|
||||
</MudButton>
|
||||
}
|
||||
</MudStack>
|
||||
</MudDrawer>
|
||||
|
||||
@code
|
||||
{
|
||||
private string? _code;
|
||||
|
||||
private bool _visible;
|
||||
private bool _showQrCode;
|
||||
|
||||
private bool _enabled;
|
||||
private int _recoveryCount;
|
||||
private List<string>? _recoveryCodes;
|
||||
private string? _authenticatorKey;
|
||||
private string? _authenticatorFormatKey;
|
||||
|
||||
public bool Visible
|
||||
{
|
||||
get
|
||||
{
|
||||
return _visible;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_visible == value) return;
|
||||
if (value) _content = Content.Options;
|
||||
|
||||
_visible = value;
|
||||
|
||||
StateHasChanged();
|
||||
|
||||
if (OnToggle is not null)
|
||||
{
|
||||
OnToggle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Toggle()
|
||||
{
|
||||
Visible = !Visible;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,217 @@
|
|||
using Insight.Infrastructure.Entities;
|
||||
using Insight.Infrastructure.Services;
|
||||
using Insight.Web.Constants;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
using MudBlazor;
|
||||
|
||||
namespace Insight.Web.Pages.Account;
|
||||
|
||||
public partial class ProfileTwoFactorDialog
|
||||
{
|
||||
[CascadingParameter(Name = "User")] public InsightUser? User { get; set; }
|
||||
|
||||
[Parameter] public Func<Task>? OnToggle { get; set; }
|
||||
|
||||
[Inject] private AuthenticatorService AuthenticatorService { get; init; } = default!;
|
||||
[Inject] private IJSRuntime JSRuntime { get; init; } = default!;
|
||||
[Inject] private ISnackbar Snackbar { get; init; } = default!;
|
||||
|
||||
private enum Content { Options, Validate, Enable, Delete, UseRecovery, ResetRecovery, ShowRecovery }
|
||||
private Content _content = Content.Options;
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
await RefreshAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RefreshAsync()
|
||||
{
|
||||
if (User is null) return;
|
||||
|
||||
_authenticatorKey = await AuthenticatorService.GetKeyAsync(User).ConfigureAwait(false);
|
||||
if (string.IsNullOrEmpty(_authenticatorKey)) // if no key exists, generate one + recovery keys
|
||||
{
|
||||
await AuthenticatorService.ResetKeyAsync(User).ConfigureAwait(false);
|
||||
_authenticatorKey = await AuthenticatorService.GetKeyAsync(User).ConfigureAwait(false);
|
||||
|
||||
var recoveryCodes = await AuthenticatorService.ResetRecoveryCodesAsync(User).ConfigureAwait(false);
|
||||
if (recoveryCodes is not null)
|
||||
{
|
||||
_recoveryCodes = new List<string>(recoveryCodes);
|
||||
}
|
||||
}
|
||||
|
||||
if (_authenticatorKey is not null) _authenticatorFormatKey = AuthenticatorService.HumanizeKey(_authenticatorKey);
|
||||
|
||||
_enabled = await AuthenticatorService.GetStatusAsync(User).ConfigureAwait(false);
|
||||
_recoveryCount = await AuthenticatorService.CountRecoveryCodesAsync(User).ConfigureAwait(false);
|
||||
|
||||
await ClearQrCodeAsync("qrc").ConfigureAwait(false);
|
||||
await CreateQrCodeAsync("qrc", 270, 270).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task OnChangeAsync(Content content)
|
||||
{
|
||||
await RefreshAsync().ConfigureAwait(false);
|
||||
|
||||
// if active site displays recovery codes, clear on switch
|
||||
if (_content == Content.ShowRecovery) _recoveryCodes = null;
|
||||
|
||||
_content = content;
|
||||
|
||||
await InvokeAsync(StateHasChanged).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task ValidateAsync(string? code)
|
||||
{
|
||||
if (User is null || string.IsNullOrWhiteSpace(code)) return;
|
||||
|
||||
var validation = await AuthenticatorService.VerifyAsync(User, code).ConfigureAwait(false);
|
||||
if (validation is false)
|
||||
{
|
||||
Notification.Error(Snackbar, "Invalid 2FA Code");
|
||||
return;
|
||||
}
|
||||
|
||||
Notification.Success(Snackbar, "Valid 2FA Code");
|
||||
|
||||
await OnChangeAsync(Content.Options).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task EnableAsync(string? code)
|
||||
{
|
||||
Console.WriteLine(_code);
|
||||
|
||||
if (User is null)
|
||||
{
|
||||
Notification.Error(Snackbar, "user null");
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(code))
|
||||
{
|
||||
Notification.Error(Snackbar, $"code null ({code})");
|
||||
return;
|
||||
}
|
||||
|
||||
var validation = await AuthenticatorService.VerifyAsync(User, code).ConfigureAwait(false);
|
||||
if (validation is false)
|
||||
{
|
||||
Notification.Error(Snackbar, "Invalid 2FA Code");
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await AuthenticatorService.EnableAsync(User).ConfigureAwait(false);
|
||||
if (result is false)
|
||||
{
|
||||
Notification.Error(Snackbar, "Error");
|
||||
return;
|
||||
}
|
||||
|
||||
Notification.Success(Snackbar, "Enabled 2FA");
|
||||
|
||||
await OnChangeAsync(Content.Options).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task DisableAsync()
|
||||
{
|
||||
if (User is null) return;
|
||||
|
||||
var result = await AuthenticatorService.DisableAsync(User).ConfigureAwait(false);
|
||||
if (result is false)
|
||||
{
|
||||
Notification.Error(Snackbar, "Error");
|
||||
return;
|
||||
}
|
||||
|
||||
Notification.Success(Snackbar, "Disabled 2FA");
|
||||
|
||||
await OnChangeAsync(Content.Options).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task DeleteAsync()
|
||||
{
|
||||
if (User is null) return;
|
||||
|
||||
var result = await AuthenticatorService.DeleteAsync(User).ConfigureAwait(false);
|
||||
if (result is false)
|
||||
{
|
||||
Notification.Error(Snackbar, "Error");
|
||||
return;
|
||||
}
|
||||
|
||||
Notification.Success(Snackbar, "Deleted 2FA");
|
||||
await OnChangeAsync(Content.Options).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task UseRecoveryAsync(string? code)
|
||||
{
|
||||
if (User is null || string.IsNullOrWhiteSpace(code)) return;
|
||||
|
||||
await CountRecoveryAsync().ConfigureAwait(false);
|
||||
|
||||
if (_recoveryCount == 0)
|
||||
{
|
||||
Notification.Error(Snackbar, "No Recovery Codes Left");
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await AuthenticatorService.UseRecoveryCodeAsync(User, code).ConfigureAwait(false);
|
||||
if (result is false)
|
||||
{
|
||||
Notification.Error(Snackbar, "Invalid Code");
|
||||
return;
|
||||
}
|
||||
|
||||
await CountRecoveryAsync().ConfigureAwait(false);
|
||||
|
||||
Notification.Success(Snackbar, $"Recovery Codes Left: ({_recoveryCount})");
|
||||
await OnChangeAsync(Content.Options).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task ResetRecoveryAsync()
|
||||
{
|
||||
if (User is null) return;
|
||||
|
||||
var recoveryCodes = await AuthenticatorService.ResetRecoveryCodesAsync(User).ConfigureAwait(false);
|
||||
if (recoveryCodes is null)
|
||||
{
|
||||
Notification.Error(Snackbar, "Error");
|
||||
return;
|
||||
}
|
||||
|
||||
if (recoveryCodes is not null)
|
||||
{
|
||||
_recoveryCodes = new List<string>(recoveryCodes);
|
||||
}
|
||||
|
||||
Notification.Success(Snackbar, "Reset 2FA Recovery Codes");
|
||||
await OnChangeAsync(Content.ShowRecovery).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task CountRecoveryAsync()
|
||||
{
|
||||
if (User is null) return;
|
||||
|
||||
_recoveryCount = await AuthenticatorService.CountRecoveryCodesAsync(User).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task CreateQrCodeAsync(string elementId, int width, int height)
|
||||
{
|
||||
if (User is null || User.Email is null) return;
|
||||
if (_authenticatorKey is null) return;
|
||||
|
||||
var code = AuthenticatorService.GenerateQrCode(User.Email, _authenticatorKey);
|
||||
|
||||
await JSRuntime.InvokeVoidAsync("createQrCode", elementId, code, width, height).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task ClearQrCodeAsync(string elementId)
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("clearQrCode", elementId).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
9
src/Web/Insight.Web/Pages/Index.razor
Normal file
9
src/Web/Insight.Web/Pages/Index.razor
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
@page "/"
|
||||
@inject NavigationManager NavManager
|
||||
|
||||
@code {
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
NavManager.NavigateTo(Navigation.Monitoring.Maintenance.Index);
|
||||
}
|
||||
}
|
||||
1
src/Web/Insight.Web/Pages/Internal/Seed.razor
Normal file
1
src/Web/Insight.Web/Pages/Internal/Seed.razor
Normal file
|
|
@ -0,0 +1 @@
|
|||
<h1>@IsSuccess</h1>
|
||||
28
src/Web/Insight.Web/Pages/Internal/Seed.razor.cs
Normal file
28
src/Web/Insight.Web/Pages/Internal/Seed.razor.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
using Insight.Infrastructure.Services;
|
||||
using Insight.Web.Constants;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace Insight.Web.Pages.Internal;
|
||||
|
||||
[Route(Navigation.Internal.Seed)]
|
||||
public partial class Seed
|
||||
{
|
||||
[Inject] private IdentityService IdentityService { get; init; } = default!;
|
||||
[Inject] private ILogger<Seed> Logger { get; init; } = default!;
|
||||
|
||||
private bool IsSuccess = false;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await IdentityService.SeedAsync();
|
||||
IsSuccess = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex.ToString());
|
||||
IsSuccess = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
50
src/Web/Insight.Web/Pages/Internal/Sessions.razor
Normal file
50
src/Web/Insight.Web/Pages/Internal/Sessions.razor
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
<div class="mt-7">
|
||||
<MudContainer MaxWidth="MaxWidth.ExtraExtraLarge">
|
||||
<MudGrid>
|
||||
<MudItem xs="12" sm="6" md="6">
|
||||
<p><b>Current Session</b></p>
|
||||
<p> @SessionHandler.State.Id</p>
|
||||
<br />
|
||||
<p><b>Current Authenticated</b></p>
|
||||
<p> @SessionHandler.State.Authenticated</p>
|
||||
<br />
|
||||
<p><b>Current User</b></p>
|
||||
<p> @SessionHandler.State.Username</p>
|
||||
<br />
|
||||
<p><b>Active User Sessions</b></p>
|
||||
@{
|
||||
foreach (var cs in SessionPool.Sessions.Where(p => p.Value.Connected && p.Value.Username == SessionHandler.State.Username))
|
||||
{
|
||||
<p>@($"{cs.Key} ({cs.Value?.Username})")</p>
|
||||
}
|
||||
}
|
||||
</MudItem>
|
||||
<MudItem xs="12" sm="6" md="6">
|
||||
<p><b>All Sessions</b></p>
|
||||
@{
|
||||
foreach (var cs in SessionPool.Sessions)
|
||||
{
|
||||
<p>@($"{cs.Key} ({cs.Value?.Username})")</p>
|
||||
}
|
||||
}
|
||||
<br />
|
||||
<p><b>Active Sessions</b></p>
|
||||
@{
|
||||
foreach (var cs in SessionPool.Sessions.Where(p => p.Value.Connected))
|
||||
{
|
||||
<p>@($"{cs.Key} ({cs.Value?.Username})")</p>
|
||||
}
|
||||
}
|
||||
<br />
|
||||
<p><b>Starving Sessions</b></p>
|
||||
@{
|
||||
foreach (var cs in SessionPool.Sessions.Where(p => p.Value.Connected is false))
|
||||
{
|
||||
<p>@($"{cs.Key} ({cs.Value?.Username})")</p>
|
||||
}
|
||||
}
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
@HttpContextAccessor.HttpContext?.Connection?.RemoteIpAddress;
|
||||
</MudContainer>
|
||||
</div>
|
||||
14
src/Web/Insight.Web/Pages/Internal/Sessions.razor.cs
Normal file
14
src/Web/Insight.Web/Pages/Internal/Sessions.razor.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
using Insight.Web.Constants;
|
||||
using Insight.Web.Services;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace Insight.Web.Pages.Internal;
|
||||
|
||||
[Route(Navigation.Internal.Sessions)]
|
||||
public partial class Sessions
|
||||
{
|
||||
[Inject] private SessionPool SessionPool { get; init; } = default!;
|
||||
[Inject] private SessionHandler SessionHandler { get; init; } = default!;
|
||||
[Inject] private IHttpContextAccessor HttpContextAccessor { get; init; } = default!;
|
||||
[Inject] private ILogger<Sessions> Logger { get; init; } = default!;
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
@inherits ComponentBase
|
||||
|
||||
<TableContainer T="ViewModel"
|
||||
@ref="Container"
|
||||
@bind-Search="Search"
|
||||
Title="@Title"
|
||||
Breadcrumbs="@Breadcrumbs"
|
||||
Data="LoadDataAsync">
|
||||
<Header>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Customer" T="ViewModel">
|
||||
Customer
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Host" T="ViewModel">
|
||||
Host
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Firmware" T="ViewModel">
|
||||
Firmware
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Serial" T="ViewModel">
|
||||
Serial
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Status" T="ViewModel">
|
||||
Status
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
</Header>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="Customer">
|
||||
<MudLink Href="@Navigation.Management.Customers.DetailsHref(@context?.Customers?.Id)" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Customers?.Name
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Host">
|
||||
<MudLink Href="@Navigation.Management.Hosts.DetailsHref(@context?.Hosts?.Id)" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Hosts?.Name
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Firmware">
|
||||
@context?.Firmware
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Serial">
|
||||
@context?.Serial
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Status">
|
||||
@{
|
||||
var color = context?.Status?.ToLower() switch
|
||||
{
|
||||
"ok" => Color.Success,
|
||||
_ => Color.Error
|
||||
};
|
||||
<MudText Color="color" Typo="Typo.inherit">
|
||||
@context?.Status
|
||||
</MudText>
|
||||
}
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</TableContainer>
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
using Insight.Infrastructure;
|
||||
using Insight.Infrastructure.Entities;
|
||||
using Insight.Web.Components.Containers;
|
||||
using Insight.Web.Constants;
|
||||
using Insight.Web.Extensions;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using MongoDB.Driver;
|
||||
using MongoDB.Driver.Linq;
|
||||
using MudBlazor;
|
||||
using System.Text.RegularExpressions;
|
||||
using SortDirection = MudBlazor.SortDirection;
|
||||
|
||||
namespace Insight.Web.Pages.Inventory.Hardware.Drives;
|
||||
|
||||
[Route(Navigation.Inventory.Hardware.Drives.Hosts)]
|
||||
public partial class Hosts
|
||||
{
|
||||
[Parameter] public string? DriveName { get; set; }
|
||||
|
||||
[Inject] private IMongoDatabase Database { get; init; } = default!;
|
||||
[Inject] private ISnackbar Snackbar { get; init; } = default!;
|
||||
[Inject] private NavigationManager NavigationManager { get; init; } = default!;
|
||||
|
||||
private TableContainer<ViewModel>? Container { get; set; }
|
||||
private string Title { get; set; } = Global.Name;
|
||||
private List<BreadcrumbItem> Breadcrumbs { get; } = new();
|
||||
private string? Search { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Home", href: Navigation.Home));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Inventory", href: "#", true));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Drives", href: Navigation.Inventory.Hardware.Drives.Index));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(DriveName))
|
||||
{
|
||||
Notification.Error(Snackbar, "Not Found");
|
||||
NavigationManager.NavigateTo(Navigation.Inventory.Hardware.Drives.Index);
|
||||
}
|
||||
|
||||
Title = $"Inventory » Drives » {DriveName} » Hosts|Insight";
|
||||
Breadcrumbs.Add(new BreadcrumbItem(DriveName, href: "#", true));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Hosts", href: "#", true));
|
||||
}
|
||||
|
||||
private async Task<TableData<ViewModel>> LoadDataAsync(TableState state)
|
||||
{
|
||||
try
|
||||
{
|
||||
var filter = Builders<BsonDocument>.Filter.Empty;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Search) is false)
|
||||
{
|
||||
var regex = new BsonRegularExpression(new Regex(Search, RegexOptions.IgnoreCase));
|
||||
filter &= Builders<BsonDocument>.Filter.Regex("customer.name", regex) |
|
||||
Builders<BsonDocument>.Filter.Regex("host.name", regex) |
|
||||
Builders<BsonDocument>.Filter.Regex("firmware", regex) |
|
||||
Builders<BsonDocument>.Filter.Regex("serial", regex) |
|
||||
Builders<BsonDocument>.Filter.Regex("status", regex);
|
||||
}
|
||||
|
||||
var query = Database.HostDrive()
|
||||
.Aggregate()
|
||||
.Match(Builders<HostDriveEntity>.Filter.Regex(p => p.Name, new BsonRegularExpression(new Regex(DriveName.Escape(), RegexOptions.IgnoreCase))))
|
||||
.Lookup("host", "_host", "_id", "hosts")
|
||||
.Match(new BsonDocument("hosts", new BsonDocument
|
||||
{
|
||||
{ "$exists", true },
|
||||
{ "$ne", new BsonArray() }
|
||||
}))
|
||||
.Lookup("customer", "hosts._customer", "_id", "customers")
|
||||
.Project(new BsonDocument
|
||||
{
|
||||
{ "_id", 0 },
|
||||
{ "firmware", 1 },
|
||||
{ "serial", 1 },
|
||||
{ "status", 1 },
|
||||
{ "host", new BsonDocument("$first", "$hosts") },
|
||||
{ "customer", new BsonDocument("$first", "$customers") },
|
||||
})
|
||||
.Match(filter)
|
||||
.Sort(state.SortDirection switch
|
||||
{
|
||||
SortDirection.Ascending => state.SortLabel switch
|
||||
{
|
||||
"Customer" => Builders<BsonDocument>.Sort.Ascending("customer.name"),
|
||||
"Host" => Builders<BsonDocument>.Sort.Ascending("host.name"),
|
||||
"Firmware" => Builders<BsonDocument>.Sort.Ascending("firmware"),
|
||||
"Serial" => Builders<BsonDocument>.Sort.Ascending("serial"),
|
||||
"Status" => Builders<BsonDocument>.Sort.Ascending("status"),
|
||||
_ => null
|
||||
},
|
||||
SortDirection.Descending => state.SortLabel switch
|
||||
{
|
||||
"Customer" => Builders<BsonDocument>.Sort.Descending("customer.name"),
|
||||
"Host" => Builders<BsonDocument>.Sort.Descending("host.name"),
|
||||
"Firmware" => Builders<BsonDocument>.Sort.Descending("firmware"),
|
||||
"Serial" => Builders<BsonDocument>.Sort.Descending("serial"),
|
||||
"Status" => Builders<BsonDocument>.Sort.Descending("status"),
|
||||
_ => null
|
||||
},
|
||||
_ => Builders<BsonDocument>.Sort.Ascending("customer.name")
|
||||
});
|
||||
|
||||
var countResult = await query.Count().FirstOrDefaultAsync(default);
|
||||
var itemResult = await query.Skip(state.Page * state.PageSize).Limit(state.PageSize).ToListAsync(default);
|
||||
|
||||
return new TableData<ViewModel>()
|
||||
{
|
||||
TotalItems = countResult is null ? 0 : (int)countResult.Count,
|
||||
Items = itemResult.Select(x => BsonSerializer.Deserialize<ViewModel>(x))
|
||||
};
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Notification.Error(Snackbar);
|
||||
return new TableData<ViewModel>();
|
||||
}
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
public class ViewModel
|
||||
{
|
||||
[BsonElement("firmware")]
|
||||
public string? Firmware { get; set; }
|
||||
|
||||
[BsonElement("serial")]
|
||||
public string? Serial { get; set; }
|
||||
|
||||
[BsonElement("status")]
|
||||
public string? Status { get; set; }
|
||||
|
||||
[BsonElement("host")]
|
||||
public HostEntity? Hosts { get; set; }
|
||||
|
||||
[BsonElement("customer")]
|
||||
public CustomerEntity? Customers { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
@inherits ComponentBase
|
||||
|
||||
<TableContainer T="ViewModel"
|
||||
@ref="Container"
|
||||
@bind-Search="Search"
|
||||
Title="@Title"
|
||||
Breadcrumbs="@Breadcrumbs"
|
||||
Data="LoadDataAsync">
|
||||
<Header>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Name" T="ViewModel">
|
||||
Name
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Type" T="ViewModel">
|
||||
Type
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Size" T="ViewModel">
|
||||
Capacity
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Hosts" T="ViewModel">
|
||||
Hosts
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
</Header>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="Name">
|
||||
@context?.Name
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Type">
|
||||
@context?.Type
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Size">
|
||||
@{
|
||||
double? size = null;
|
||||
var value = "-";
|
||||
|
||||
if (context?.Capacity is not null)
|
||||
{
|
||||
size = Math.Round((context.Capacity.Value / Math.Pow(1024, 3)), 2);
|
||||
value = $"{size} GB";
|
||||
}
|
||||
|
||||
@value
|
||||
}
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Hosts">
|
||||
<MudLink Href="@Navigation.Inventory.Hardware.Drives.HostsHref(context?.Name)" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Hosts
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</TableContainer>
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
using Insight.Infrastructure;
|
||||
using Insight.Infrastructure.Entities;
|
||||
using Insight.Web.Components.Containers;
|
||||
using Insight.Web.Constants;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using MongoDB.Driver;
|
||||
using MudBlazor;
|
||||
using System.Text.RegularExpressions;
|
||||
using SortDirection = MudBlazor.SortDirection;
|
||||
|
||||
namespace Insight.Web.Pages.Inventory.Hardware.Drives;
|
||||
|
||||
[Route(Navigation.Inventory.Hardware.Drives.Index)]
|
||||
public partial class Index
|
||||
{
|
||||
[Inject] private IMongoDatabase Database { get; init; } = default!;
|
||||
[Inject] private ISnackbar Snackbar { get; init; } = default!;
|
||||
|
||||
private TableContainer<ViewModel>? Container { get; set; }
|
||||
private string Title { get; set; } = Global.Name;
|
||||
private List<BreadcrumbItem> Breadcrumbs { get; } = new();
|
||||
private string? Search { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Title = $"Inventory » Drives|Insight";
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Home", href: Navigation.Home));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Inventory", href: "#", true));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Drives", href: "#", true));
|
||||
}
|
||||
|
||||
private async Task<TableData<ViewModel>> LoadDataAsync(TableState state)
|
||||
{
|
||||
try
|
||||
{
|
||||
var filter = Builders<HostDriveEntity>.Filter.Empty;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Search) is false)
|
||||
{
|
||||
var regex = new BsonRegularExpression(new Regex(Search, RegexOptions.IgnoreCase));
|
||||
filter &= Builders<HostDriveEntity>.Filter.Regex(x => x.Company, regex) |
|
||||
Builders<HostDriveEntity>.Filter.Regex(x => x.Name, regex);
|
||||
}
|
||||
|
||||
var query = Database.HostDrive()
|
||||
.Aggregate()
|
||||
.Match(filter)
|
||||
.Lookup("host", "_host", "_id", "hosts")
|
||||
.Match(new BsonDocument("hosts", new BsonDocument
|
||||
{
|
||||
{ "$exists", true },
|
||||
{ "$ne", new BsonArray() }
|
||||
}))
|
||||
.Group(new BsonDocument
|
||||
{
|
||||
{ "_id", new BsonDocument("name", "$name") },
|
||||
{ "name", new BsonDocument("$first", "$name") },
|
||||
{ "type", new BsonDocument("$first", "$type") },
|
||||
{ "size", new BsonDocument("$first", "$size") },
|
||||
{ "hosts", new BsonDocument("$addToSet", "$hosts") },
|
||||
})
|
||||
.Project(new BsonDocument
|
||||
{
|
||||
{ "_id", 0 },
|
||||
{ "name", 1 },
|
||||
{ "type", 1 },
|
||||
{ "size", 1 },
|
||||
{ "hosts", 1 },
|
||||
{ "hosts_size", new BsonDocument("$size", "$hosts") },
|
||||
})
|
||||
.Sort(state.SortDirection switch
|
||||
{
|
||||
SortDirection.Ascending => state.SortLabel switch
|
||||
{
|
||||
"Name" => Builders<BsonDocument>.Sort.Ascending("name"),
|
||||
"Type" => Builders<BsonDocument>.Sort.Ascending("type"),
|
||||
"Size" => Builders<BsonDocument>.Sort.Ascending("size"),
|
||||
"Hosts" => Builders<BsonDocument>.Sort.Ascending("hosts_size"),
|
||||
_ => null
|
||||
},
|
||||
SortDirection.Descending => state.SortLabel switch
|
||||
{
|
||||
"Name" => Builders<BsonDocument>.Sort.Descending("name"),
|
||||
"Type" => Builders<BsonDocument>.Sort.Descending("type"),
|
||||
"Size" => Builders<BsonDocument>.Sort.Descending("size"),
|
||||
"Hosts" => Builders<BsonDocument>.Sort.Descending("hosts_size"),
|
||||
_ => null
|
||||
},
|
||||
_ => Builders<BsonDocument>.Sort.Descending("hosts_size")
|
||||
});
|
||||
|
||||
var countResult = await query.Count().FirstOrDefaultAsync(default);
|
||||
var itemResult = await query.Skip(state.Page * state.PageSize).Limit(state.PageSize).ToListAsync(default);
|
||||
|
||||
return new TableData<ViewModel>()
|
||||
{
|
||||
TotalItems = countResult is null ? 0 : (int)countResult.Count,
|
||||
Items = itemResult.Select(x => BsonSerializer.Deserialize<ViewModel>(x))
|
||||
};
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Notification.Error(Snackbar);
|
||||
return new TableData<ViewModel>();
|
||||
}
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
public class ViewModel
|
||||
{
|
||||
[BsonElement("name")]
|
||||
public string? Name { get; set; }
|
||||
|
||||
[BsonElement("type")]
|
||||
public string? Type { get; set; }
|
||||
|
||||
[BsonElement("size")]
|
||||
public long? Capacity { get; set; }
|
||||
|
||||
[BsonElement("hosts_size")]
|
||||
public int? Hosts { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
@inherits ComponentBase
|
||||
|
||||
<TableContainer T="ViewModel"
|
||||
@ref="Container"
|
||||
@bind-Search="Search"
|
||||
Title="@Title"
|
||||
Breadcrumbs="@Breadcrumbs"
|
||||
Data="LoadDataAsync">
|
||||
<Header>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Customer" T="ViewModel">
|
||||
Customer
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Host" T="ViewModel">
|
||||
Host
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Version" T="ViewModel">
|
||||
Version
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Serial" T="ViewModel">
|
||||
Serial
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
</Header>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="Customer">
|
||||
<MudLink Href="@Navigation.Management.Customers.DetailsHref(@context?.Customers?.Id)" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Customers?.Name
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Host">
|
||||
<MudLink Href="@Navigation.Management.Hosts.DetailsHref(@context?.Hosts?.Id)" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Hosts?.Name
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Version">
|
||||
@context?.Version
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Version">
|
||||
@context?.Serial
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</TableContainer>
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
using Insight.Infrastructure;
|
||||
using Insight.Infrastructure.Entities;
|
||||
using Insight.Web.Components.Containers;
|
||||
using Insight.Web.Constants;
|
||||
using Insight.Web.Extensions;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using MongoDB.Driver;
|
||||
using MongoDB.Driver.Linq;
|
||||
using MudBlazor;
|
||||
using System.Text.RegularExpressions;
|
||||
using SortDirection = MudBlazor.SortDirection;
|
||||
|
||||
namespace Insight.Web.Pages.Inventory.Hardware.Mainboards;
|
||||
|
||||
[Route(Navigation.Inventory.Hardware.Mainboards.Hosts)]
|
||||
public partial class Hosts
|
||||
{
|
||||
[Parameter] public string? MainboardName { get; set; }
|
||||
|
||||
[Inject] private IMongoDatabase Database { get; init; } = default!;
|
||||
[Inject] private ISnackbar Snackbar { get; init; } = default!;
|
||||
[Inject] private NavigationManager NavigationManager { get; init; } = default!;
|
||||
|
||||
private TableContainer<ViewModel>? Container { get; set; }
|
||||
private string Title { get; set; } = Global.Name;
|
||||
private List<BreadcrumbItem> Breadcrumbs { get; } = new();
|
||||
private string? Search { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Home", href: Navigation.Home));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Inventory", href: "#", true));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Mainboards", href: Navigation.Inventory.Hardware.Mainboards.Index));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(MainboardName))
|
||||
{
|
||||
Notification.Error(Snackbar, "Not Found");
|
||||
NavigationManager.NavigateTo(Navigation.Inventory.Hardware.Mainboards.Index);
|
||||
return;
|
||||
}
|
||||
|
||||
Title = $"Inventory » Mainboards » {MainboardName} » Hosts|Insight";
|
||||
Breadcrumbs.Add(new BreadcrumbItem(MainboardName, href: "#", true));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Hosts", href: "#", true));
|
||||
}
|
||||
|
||||
private async Task<TableData<ViewModel>> LoadDataAsync(TableState state)
|
||||
{
|
||||
try
|
||||
{
|
||||
var filter = Builders<BsonDocument>.Filter.Empty;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Search) is false)
|
||||
{
|
||||
var regex = new BsonRegularExpression(new Regex(Search, RegexOptions.IgnoreCase));
|
||||
filter &= Builders<BsonDocument>.Filter.Regex("customer.name", regex) |
|
||||
Builders<BsonDocument>.Filter.Regex("host.name", regex) |
|
||||
Builders<BsonDocument>.Filter.Regex("version", regex);
|
||||
}
|
||||
|
||||
var query = Database.HostMainboard()
|
||||
.Aggregate()
|
||||
.Match(Builders<HostMainboardEntity>.Filter.Regex(p => p.Name, new BsonRegularExpression(new Regex(MainboardName.Escape(), RegexOptions.IgnoreCase))))
|
||||
.Lookup("host", "_host", "_id", "hosts")
|
||||
.Match(new BsonDocument("hosts", new BsonDocument
|
||||
{
|
||||
{ "$exists", true },
|
||||
{ "$ne", new BsonArray() }
|
||||
}))
|
||||
.Lookup("customer", "hosts._customer", "_id", "customers")
|
||||
.Project(new BsonDocument
|
||||
{
|
||||
{ "_id", 0 },
|
||||
{ "version", 1 },
|
||||
{ "serial", 1 },
|
||||
{ "host", new BsonDocument("$first", "$hosts") },
|
||||
{ "customer", new BsonDocument("$first", "$customers") },
|
||||
})
|
||||
.Match(filter)
|
||||
.Sort(state.SortDirection switch
|
||||
{
|
||||
SortDirection.Ascending => state.SortLabel switch
|
||||
{
|
||||
"Customer" => Builders<BsonDocument>.Sort.Ascending("customer.name"),
|
||||
"Host" => Builders<BsonDocument>.Sort.Ascending("host.name"),
|
||||
"Version" => Builders<BsonDocument>.Sort.Ascending("version"),
|
||||
"Serial" => Builders<BsonDocument>.Sort.Ascending("serial"),
|
||||
_ => null
|
||||
},
|
||||
SortDirection.Descending => state.SortLabel switch
|
||||
{
|
||||
"Customer" => Builders<BsonDocument>.Sort.Descending("customer.name"),
|
||||
"Host" => Builders<BsonDocument>.Sort.Descending("host.name"),
|
||||
"Version" => Builders<BsonDocument>.Sort.Descending("version"),
|
||||
"Serial" => Builders<BsonDocument>.Sort.Descending("serial"),
|
||||
_ => null
|
||||
},
|
||||
_ => Builders<BsonDocument>.Sort.Ascending("customer.name")
|
||||
});
|
||||
|
||||
var countResult = await query.Count().FirstOrDefaultAsync(default);
|
||||
var itemResult = await query.Skip(state.Page * state.PageSize).Limit(state.PageSize).ToListAsync(default);
|
||||
|
||||
return new TableData<ViewModel>()
|
||||
{
|
||||
TotalItems = countResult is null ? 0 : (int)countResult.Count,
|
||||
Items = itemResult.Select(x => BsonSerializer.Deserialize<ViewModel>(x))
|
||||
};
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Notification.Error(Snackbar);
|
||||
return new TableData<ViewModel>();
|
||||
}
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
public class ViewModel
|
||||
{
|
||||
[BsonElement("version")]
|
||||
public string? Version { get; set; }
|
||||
|
||||
[BsonElement("serial")]
|
||||
public string? Serial { get; set; }
|
||||
|
||||
[BsonElement("host")]
|
||||
public HostEntity? Hosts { get; set; }
|
||||
|
||||
[BsonElement("customer")]
|
||||
public CustomerEntity? Customers { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
@inherits ComponentBase
|
||||
|
||||
<TableContainer T="ViewModel"
|
||||
@ref="Container"
|
||||
@bind-Search="Search"
|
||||
Title="@Title"
|
||||
Breadcrumbs="@Breadcrumbs"
|
||||
Data="LoadDataAsync">
|
||||
<Header>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Name" T="ViewModel">
|
||||
Name
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Bios" T="ViewModel">
|
||||
Bios
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Hosts" T="ViewModel">
|
||||
Hosts
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
</Header>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="Name">
|
||||
@context?.Name
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Bios">
|
||||
@context?.Bios
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Hosts">
|
||||
<MudLink Href="@Navigation.Inventory.Hardware.Mainboards.HostsHref(context?.Name)" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Hosts
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</TableContainer>
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
using Insight.Infrastructure;
|
||||
using Insight.Infrastructure.Entities;
|
||||
using Insight.Web.Components.Containers;
|
||||
using Insight.Web.Constants;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using MongoDB.Driver;
|
||||
using MudBlazor;
|
||||
using System.Text.RegularExpressions;
|
||||
using SortDirection = MudBlazor.SortDirection;
|
||||
|
||||
namespace Insight.Web.Pages.Inventory.Hardware.Mainboards;
|
||||
|
||||
[Route(Navigation.Inventory.Hardware.Mainboards.Index)]
|
||||
public partial class Index
|
||||
{
|
||||
[Inject] private IMongoDatabase Database { get; init; } = default!;
|
||||
[Inject] private ISnackbar Snackbar { get; init; } = default!;
|
||||
|
||||
private TableContainer<ViewModel>? Container { get; set; }
|
||||
private string Title { get; set; } = Global.Name;
|
||||
private List<BreadcrumbItem> Breadcrumbs { get; } = new();
|
||||
private string? Search { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Title = $"Inventory » Mainboards|Insight";
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Home", href: Navigation.Home));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Inventory", href: "#", true));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Mainboards", href: "#", true));
|
||||
}
|
||||
|
||||
private async Task<TableData<ViewModel>> LoadDataAsync(TableState state)
|
||||
{
|
||||
try
|
||||
{
|
||||
var filter = Builders<HostMainboardEntity>.Filter.Empty;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Search) is false)
|
||||
{
|
||||
var regex = new BsonRegularExpression(new Regex(Search, RegexOptions.IgnoreCase));
|
||||
filter &= Builders<HostMainboardEntity>.Filter.Regex(x => x.Bios, regex) |
|
||||
Builders<HostMainboardEntity>.Filter.Regex(x => x.Name, regex);
|
||||
}
|
||||
|
||||
var query = Database.HostMainboard()
|
||||
.Aggregate()
|
||||
.Match(filter)
|
||||
.Lookup("host", "_host", "_id", "hosts")
|
||||
.Match(new BsonDocument("hosts", new BsonDocument
|
||||
{
|
||||
{ "$exists", true },
|
||||
{ "$ne", new BsonArray() }
|
||||
}))
|
||||
.Group(new BsonDocument
|
||||
{
|
||||
{ "_id", new BsonDocument("name", "$name") },
|
||||
{ "bios", new BsonDocument("$first", "$bios") },
|
||||
{ "name", new BsonDocument("$first", "$name") },
|
||||
{ "hosts", new BsonDocument("$addToSet", "$hosts") },
|
||||
})
|
||||
.Project(new BsonDocument
|
||||
{
|
||||
{ "_id", 0 },
|
||||
{ "bios", 1 },
|
||||
{ "name", 1 },
|
||||
{ "hosts", 1 },
|
||||
{ "hosts_size", new BsonDocument("$size", "$hosts") },
|
||||
})
|
||||
.Sort(state.SortDirection switch
|
||||
{
|
||||
SortDirection.Ascending => state.SortLabel switch
|
||||
{
|
||||
"Bios" => Builders<BsonDocument>.Sort.Ascending("bios"),
|
||||
"Name" => Builders<BsonDocument>.Sort.Ascending("name"),
|
||||
"Hosts" => Builders<BsonDocument>.Sort.Ascending("hosts_size"),
|
||||
_ => null
|
||||
},
|
||||
SortDirection.Descending => state.SortLabel switch
|
||||
{
|
||||
"Bios" => Builders<BsonDocument>.Sort.Descending("bios"),
|
||||
"Name" => Builders<BsonDocument>.Sort.Descending("name"),
|
||||
"Hosts" => Builders<BsonDocument>.Sort.Descending("hosts_size"),
|
||||
_ => null
|
||||
},
|
||||
_ => Builders<BsonDocument>.Sort.Descending("hosts_size")
|
||||
});
|
||||
|
||||
var countResult = await query.Count().FirstOrDefaultAsync(default);
|
||||
var itemResult = await query.Skip(state.Page * state.PageSize).Limit(state.PageSize).ToListAsync(default);
|
||||
|
||||
return new TableData<ViewModel>()
|
||||
{
|
||||
TotalItems = countResult is null ? 0 : (int)countResult.Count,
|
||||
Items = itemResult.Select(x => BsonSerializer.Deserialize<ViewModel>(x))
|
||||
};
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Notification.Error(Snackbar);
|
||||
return new TableData<ViewModel>();
|
||||
}
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
public class ViewModel
|
||||
{
|
||||
[BsonElement("bios")]
|
||||
public string? Bios { get; set; }
|
||||
|
||||
[BsonElement("name")]
|
||||
public string? Name { get; set; }
|
||||
|
||||
[BsonElement("hosts_size")]
|
||||
public int? Hosts { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
@inherits ComponentBase
|
||||
|
||||
<TableContainer T="ViewModel"
|
||||
@ref="Container"
|
||||
@bind-Search="Search"
|
||||
Title="@Title"
|
||||
Breadcrumbs="@Breadcrumbs"
|
||||
Data="LoadDataAsync">
|
||||
<Header>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Customer" T="ViewModel">
|
||||
Customer
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Host" T="ViewModel">
|
||||
Host
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Location" T="ViewModel">
|
||||
Location
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Serial" T="ViewModel">
|
||||
Serial
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
</Header>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="Customer">
|
||||
<MudLink Href="@Navigation.Management.Customers.DetailsHref(@context?.Customers?.Id)" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Customers?.Name
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Host">
|
||||
<MudLink Href="@Navigation.Management.Hosts.DetailsHref(@context?.Hosts?.Id)" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Hosts?.Name
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Location">
|
||||
@context?.Location
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Serial">
|
||||
@context?.Serial
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</TableContainer>
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
using Insight.Infrastructure;
|
||||
using Insight.Infrastructure.Entities;
|
||||
using Insight.Web.Components.Containers;
|
||||
using Insight.Web.Constants;
|
||||
using Insight.Web.Extensions;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using MongoDB.Driver;
|
||||
using MongoDB.Driver.Linq;
|
||||
using MudBlazor;
|
||||
using System.Text.RegularExpressions;
|
||||
using SortDirection = MudBlazor.SortDirection;
|
||||
|
||||
namespace Insight.Web.Pages.Inventory.Hardware.Memory;
|
||||
|
||||
[Route(Navigation.Inventory.Hardware.Memory.Hosts)]
|
||||
public partial class Hosts
|
||||
{
|
||||
[Parameter] public string? MemoryName { get; set; }
|
||||
|
||||
[Inject] private IMongoDatabase Database { get; init; } = default!;
|
||||
[Inject] private ISnackbar Snackbar { get; init; } = default!;
|
||||
[Inject] private NavigationManager NavigationManager { get; init; } = default!;
|
||||
|
||||
private TableContainer<ViewModel>? Container { get; set; }
|
||||
private string Title { get; set; } = Global.Name;
|
||||
private List<BreadcrumbItem> Breadcrumbs { get; } = new();
|
||||
private string? Search { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Home", href: Navigation.Home));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Inventory", href: "#", true));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Memory", href: Navigation.Inventory.Hardware.Memory.Index));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(MemoryName))
|
||||
{
|
||||
Notification.Error(Snackbar, "Not Found");
|
||||
NavigationManager.NavigateTo(Navigation.Inventory.Hardware.Memory.Index);
|
||||
return;
|
||||
}
|
||||
|
||||
Title = $"Inventory » Memory » {MemoryName} » Hosts|Insight";
|
||||
Breadcrumbs.Add(new BreadcrumbItem(MemoryName, href: "#", true));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Hosts", href: "#", true));
|
||||
}
|
||||
|
||||
private async Task<TableData<ViewModel>> LoadDataAsync(TableState state)
|
||||
{
|
||||
try
|
||||
{
|
||||
var filter = Builders<BsonDocument>.Filter.Empty;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Search) is false)
|
||||
{
|
||||
var regex = new BsonRegularExpression(new Regex(Search, RegexOptions.IgnoreCase));
|
||||
filter &= Builders<BsonDocument>.Filter.Regex("customer.name", regex) |
|
||||
Builders<BsonDocument>.Filter.Regex("host.name", regex) |
|
||||
Builders<BsonDocument>.Filter.Regex("location", regex) |
|
||||
Builders<BsonDocument>.Filter.Regex("serial", regex);
|
||||
}
|
||||
|
||||
var query = Database.HostMemory()
|
||||
.Aggregate()
|
||||
.Match(Builders<HostMemoryEntity>.Filter.Regex(p => p.Name, new BsonRegularExpression(new Regex(MemoryName.Escape(), RegexOptions.IgnoreCase))))
|
||||
.Lookup("host", "_host", "_id", "hosts")
|
||||
.Match(new BsonDocument("hosts", new BsonDocument
|
||||
{
|
||||
{ "$exists", true },
|
||||
{ "$ne", new BsonArray() }
|
||||
}))
|
||||
.Lookup("customer", "hosts._customer", "_id", "customers")
|
||||
.Project(new BsonDocument
|
||||
{
|
||||
{ "_id", 0 },
|
||||
{ "location", 1 },
|
||||
{ "serial", 1 },
|
||||
{ "host", new BsonDocument("$first", "$hosts") },
|
||||
{ "customer", new BsonDocument("$first", "$customers") },
|
||||
})
|
||||
.Match(filter)
|
||||
.Sort(state.SortDirection switch
|
||||
{
|
||||
SortDirection.Ascending => state.SortLabel switch
|
||||
{
|
||||
"Customer" => Builders<BsonDocument>.Sort.Ascending("customer.name"),
|
||||
"Host" => Builders<BsonDocument>.Sort.Ascending("host.name"),
|
||||
"Location" => Builders<BsonDocument>.Sort.Ascending("location"),
|
||||
"Serial" => Builders<BsonDocument>.Sort.Ascending("serial"),
|
||||
_ => null
|
||||
},
|
||||
SortDirection.Descending => state.SortLabel switch
|
||||
{
|
||||
"Customer" => Builders<BsonDocument>.Sort.Descending("customer.name"),
|
||||
"Host" => Builders<BsonDocument>.Sort.Descending("host.name"),
|
||||
"Location" => Builders<BsonDocument>.Sort.Descending("location"),
|
||||
"Serial" => Builders<BsonDocument>.Sort.Descending("serial"),
|
||||
_ => null
|
||||
},
|
||||
_ => Builders<BsonDocument>.Sort.Ascending("customer.name")
|
||||
});
|
||||
|
||||
var countResult = await query.Count().FirstOrDefaultAsync(default);
|
||||
var itemResult = await query.Skip(state.Page * state.PageSize).Limit(state.PageSize).ToListAsync(default);
|
||||
|
||||
return new TableData<ViewModel>()
|
||||
{
|
||||
TotalItems = countResult is null ? 0 : (int)countResult.Count,
|
||||
Items = itemResult.Select(x => BsonSerializer.Deserialize<ViewModel>(x))
|
||||
};
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Notification.Error(Snackbar);
|
||||
return new TableData<ViewModel>();
|
||||
}
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
public class ViewModel
|
||||
{
|
||||
[BsonElement("location")]
|
||||
public string? Location { get; set; }
|
||||
|
||||
[BsonElement("serial")]
|
||||
public string? Serial { get; set; }
|
||||
|
||||
[BsonElement("host")]
|
||||
public HostEntity? Hosts { get; set; }
|
||||
|
||||
[BsonElement("customer")]
|
||||
public CustomerEntity? Customers { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
@inherits ComponentBase
|
||||
|
||||
<TableContainer T="ViewModel"
|
||||
@ref="Container"
|
||||
@bind-Search="Search"
|
||||
Title="@Title"
|
||||
Breadcrumbs="@Breadcrumbs"
|
||||
Data="LoadDataAsync">
|
||||
<Header>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Company" T="ViewModel">
|
||||
Company
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Name" T="ViewModel">
|
||||
Name
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Hosts" T="ViewModel">
|
||||
Hosts
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
</Header>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="Company">
|
||||
@context?.Company
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Name">
|
||||
@context?.Name
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Hosts">
|
||||
<MudLink Href="@Navigation.Inventory.Hardware.Memory.HostsHref(context?.Name)" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Hosts
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</TableContainer>
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
using Insight.Infrastructure;
|
||||
using Insight.Infrastructure.Entities;
|
||||
using Insight.Web.Components.Containers;
|
||||
using Insight.Web.Constants;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using MongoDB.Driver;
|
||||
using MudBlazor;
|
||||
using System.Text.RegularExpressions;
|
||||
using SortDirection = MudBlazor.SortDirection;
|
||||
|
||||
namespace Insight.Web.Pages.Inventory.Hardware.Memory;
|
||||
|
||||
[Route(Navigation.Inventory.Hardware.Memory.Index)]
|
||||
public partial class Index
|
||||
{
|
||||
[Inject] private IMongoDatabase Database { get; init; } = default!;
|
||||
[Inject] private ISnackbar Snackbar { get; init; } = default!;
|
||||
|
||||
private TableContainer<ViewModel>? Container { get; set; }
|
||||
private string Title { get; set; } = Global.Name;
|
||||
private List<BreadcrumbItem> Breadcrumbs { get; } = new();
|
||||
private string? Search { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Title = $"Inventory » Memory|Insight";
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Home", href: Navigation.Home));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Inventory", href: "#", true));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Memory", href: "#", true));
|
||||
}
|
||||
|
||||
private async Task<TableData<ViewModel>> LoadDataAsync(TableState state)
|
||||
{
|
||||
try
|
||||
{
|
||||
var filter = Builders<HostMemoryEntity>.Filter.Empty;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Search) is false)
|
||||
{
|
||||
var regex = new BsonRegularExpression(new Regex(Search, RegexOptions.IgnoreCase));
|
||||
filter &= Builders<HostMemoryEntity>.Filter.Regex(x => x.Company, regex) |
|
||||
Builders<HostMemoryEntity>.Filter.Regex(x => x.Name, regex);
|
||||
}
|
||||
|
||||
var query = Database.HostMemory()
|
||||
.Aggregate()
|
||||
.Match(filter)
|
||||
.Lookup("host", "_host", "_id", "hosts")
|
||||
.Match(new BsonDocument("hosts", new BsonDocument
|
||||
{
|
||||
{ "$exists", true },
|
||||
{ "$ne", new BsonArray() }
|
||||
}))
|
||||
.Group(new BsonDocument
|
||||
{
|
||||
{ "_id", new BsonDocument("name", "$name") },
|
||||
{ "company", new BsonDocument("$first", "$company") },
|
||||
{ "name", new BsonDocument("$first", "$name") },
|
||||
{ "hosts", new BsonDocument("$addToSet", "$hosts") },
|
||||
})
|
||||
.Project(new BsonDocument
|
||||
{
|
||||
{ "_id", 0 },
|
||||
{ "company", 1 },
|
||||
{ "name", 1 },
|
||||
{ "hosts", 1 },
|
||||
{ "hosts_size", new BsonDocument("$size", "$hosts") },
|
||||
})
|
||||
.Sort(state.SortDirection switch
|
||||
{
|
||||
SortDirection.Ascending => state.SortLabel switch
|
||||
{
|
||||
"Company" => Builders<BsonDocument>.Sort.Ascending("company"),
|
||||
"Name" => Builders<BsonDocument>.Sort.Ascending("name"),
|
||||
"Hosts" => Builders<BsonDocument>.Sort.Ascending("hosts_size"),
|
||||
_ => null
|
||||
},
|
||||
SortDirection.Descending => state.SortLabel switch
|
||||
{
|
||||
"Company" => Builders<BsonDocument>.Sort.Descending("company"),
|
||||
"Name" => Builders<BsonDocument>.Sort.Descending("name"),
|
||||
"Hosts" => Builders<BsonDocument>.Sort.Descending("hosts_size"),
|
||||
_ => null
|
||||
},
|
||||
_ => Builders<BsonDocument>.Sort.Descending("hosts_size")
|
||||
});
|
||||
|
||||
var countResult = await query.Count().FirstOrDefaultAsync(default);
|
||||
var itemResult = await query.Skip(state.Page * state.PageSize).Limit(state.PageSize).ToListAsync(default);
|
||||
|
||||
return new TableData<ViewModel>()
|
||||
{
|
||||
TotalItems = countResult is null ? 0 : (int)countResult.Count,
|
||||
Items = itemResult.Select(x => BsonSerializer.Deserialize<ViewModel>(x))
|
||||
};
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Notification.Error(Snackbar);
|
||||
return new TableData<ViewModel>();
|
||||
}
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
public class ViewModel
|
||||
{
|
||||
[BsonElement("company")]
|
||||
public string? Company { get; set; }
|
||||
|
||||
[BsonElement("name")]
|
||||
public string? Name { get; set; }
|
||||
|
||||
[BsonElement("hosts_size")]
|
||||
public int? Hosts { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
@inherits ComponentBase
|
||||
|
||||
<TableContainer T="ViewModel"
|
||||
@ref="Container"
|
||||
@bind-Search="Search"
|
||||
Title="@Title"
|
||||
Breadcrumbs="@Breadcrumbs"
|
||||
Data="LoadDataAsync">
|
||||
<Header>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Customer" T="ViewModel">
|
||||
Customer
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Host" T="ViewModel">
|
||||
Host
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Version" T="ViewModel">
|
||||
Version
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Serial" T="ViewModel">
|
||||
Serial
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
</Header>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="Customer">
|
||||
<MudLink Href="@Navigation.Management.Customers.DetailsHref(@context?.Customers?.Id)" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Customers?.Name
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Host">
|
||||
<MudLink Href="@Navigation.Management.Hosts.DetailsHref(@context?.Hosts?.Id)" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Hosts?.Name
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Version">
|
||||
@context?.Version
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Serial">
|
||||
@context?.Serial
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</TableContainer>
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
using Insight.Infrastructure;
|
||||
using Insight.Infrastructure.Entities;
|
||||
using Insight.Web.Components.Containers;
|
||||
using Insight.Web.Constants;
|
||||
using Insight.Web.Extensions;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using MongoDB.Driver;
|
||||
using MongoDB.Driver.Linq;
|
||||
using MudBlazor;
|
||||
using System.Text.RegularExpressions;
|
||||
using SortDirection = MudBlazor.SortDirection;
|
||||
|
||||
namespace Insight.Web.Pages.Inventory.Hardware.Processors;
|
||||
|
||||
[Route(Navigation.Inventory.Hardware.Processors.Hosts)]
|
||||
public partial class Hosts
|
||||
{
|
||||
[Parameter] public string? ProcessorName { get; set; }
|
||||
|
||||
[Inject] private IMongoDatabase Database { get; init; } = default!;
|
||||
[Inject] private ISnackbar Snackbar { get; init; } = default!;
|
||||
[Inject] private NavigationManager NavigationManager { get; init; } = default!;
|
||||
|
||||
private TableContainer<ViewModel>? Container { get; set; }
|
||||
private string Title { get; set; } = Global.Name;
|
||||
private List<BreadcrumbItem> Breadcrumbs { get; } = new();
|
||||
private string? Search { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Home", href: Navigation.Home));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Inventory", href: "#", true));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Processors", href: Navigation.Inventory.Hardware.Processors.Index));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ProcessorName))
|
||||
{
|
||||
Notification.Error(Snackbar, "Not Found");
|
||||
NavigationManager.NavigateTo(Navigation.Inventory.Hardware.Processors.Index);
|
||||
return;
|
||||
}
|
||||
|
||||
Title = $"Inventory » Processors » {ProcessorName} » Hosts|Insight";
|
||||
Breadcrumbs.Add(new BreadcrumbItem(ProcessorName, href: "#", true));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Hosts", href: "#", true));
|
||||
}
|
||||
|
||||
private async Task<TableData<ViewModel>> LoadDataAsync(TableState state)
|
||||
{
|
||||
try
|
||||
{
|
||||
var filter = Builders<BsonDocument>.Filter.Empty;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Search) is false)
|
||||
{
|
||||
var regex = new BsonRegularExpression(new Regex(Search, RegexOptions.IgnoreCase));
|
||||
filter &= Builders<BsonDocument>.Filter.Regex("customer.name", regex) |
|
||||
Builders<BsonDocument>.Filter.Regex("host.name", regex) |
|
||||
Builders<BsonDocument>.Filter.Regex("version", regex) |
|
||||
Builders<BsonDocument>.Filter.Regex("serial", regex);
|
||||
}
|
||||
|
||||
var query = Database.HostProcessor()
|
||||
.Aggregate()
|
||||
.Match(Builders<HostProcessorEntity>.Filter.Regex(p => p.Name, new BsonRegularExpression(new Regex(ProcessorName.Escape(), RegexOptions.IgnoreCase))))
|
||||
.Lookup("host", "_host", "_id", "hosts")
|
||||
.Match(new BsonDocument("hosts", new BsonDocument
|
||||
{
|
||||
{ "$exists", true },
|
||||
{ "$ne", new BsonArray() }
|
||||
}))
|
||||
.Lookup("customer", "hosts._customer", "_id", "customers")
|
||||
.Project(new BsonDocument
|
||||
{
|
||||
{ "_id", 0 },
|
||||
{ "version", 1 },
|
||||
{ "serial", 1 },
|
||||
{ "host", new BsonDocument("$first", "$hosts") },
|
||||
{ "customer", new BsonDocument("$first", "$customers") },
|
||||
})
|
||||
.Match(filter)
|
||||
.Sort(state.SortDirection switch
|
||||
{
|
||||
SortDirection.Ascending => state.SortLabel switch
|
||||
{
|
||||
"Customer" => Builders<BsonDocument>.Sort.Ascending("customer.name"),
|
||||
"Host" => Builders<BsonDocument>.Sort.Ascending("host.name"),
|
||||
"Version" => Builders<BsonDocument>.Sort.Ascending("version"),
|
||||
"Serial" => Builders<BsonDocument>.Sort.Ascending("serial"),
|
||||
_ => null
|
||||
},
|
||||
SortDirection.Descending => state.SortLabel switch
|
||||
{
|
||||
"Customer" => Builders<BsonDocument>.Sort.Descending("customer.name"),
|
||||
"Host" => Builders<BsonDocument>.Sort.Descending("host.name"),
|
||||
"Version" => Builders<BsonDocument>.Sort.Descending("version"),
|
||||
"Serial" => Builders<BsonDocument>.Sort.Descending("serial"),
|
||||
_ => null
|
||||
},
|
||||
_ => Builders<BsonDocument>.Sort.Ascending("customer.name")
|
||||
});
|
||||
|
||||
var countResult = await query.Count().FirstOrDefaultAsync(default);
|
||||
var itemResult = await query.Skip(state.Page * state.PageSize).Limit(state.PageSize).ToListAsync(default);
|
||||
|
||||
return new TableData<ViewModel>()
|
||||
{
|
||||
TotalItems = countResult is null ? 0 : (int)countResult.Count,
|
||||
Items = itemResult.Select(x => BsonSerializer.Deserialize<ViewModel>(x))
|
||||
};
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Notification.Error(Snackbar);
|
||||
return new TableData<ViewModel>();
|
||||
}
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
public class ViewModel
|
||||
{
|
||||
[BsonElement("version")]
|
||||
public string? Version { get; set; }
|
||||
|
||||
[BsonElement("serial")]
|
||||
public string? Serial { get; set; }
|
||||
|
||||
[BsonElement("host")]
|
||||
public HostEntity? Hosts { get; set; }
|
||||
|
||||
[BsonElement("customer")]
|
||||
public CustomerEntity? Customers { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
@inherits ComponentBase
|
||||
|
||||
<TableContainer T="IndexViewModel"
|
||||
@ref="Container"
|
||||
@bind-Search="Search"
|
||||
Title="@Title"
|
||||
Breadcrumbs="@Breadcrumbs"
|
||||
Data="LoadDataAsync">
|
||||
<Header>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Company" T="IndexViewModel">
|
||||
Company
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Name" T="IndexViewModel">
|
||||
Name
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Hosts" T="IndexViewModel">
|
||||
Hosts
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
</Header>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="Company">
|
||||
@context?.Company
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Name">
|
||||
@context?.Name
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Hosts">
|
||||
<MudLink Href="@Navigation.Inventory.Hardware.Processors.HostsHref(context?.Name)" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Hosts
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</TableContainer>
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
using Insight.Infrastructure;
|
||||
using Insight.Infrastructure.Entities;
|
||||
using Insight.Web.Components.Containers;
|
||||
using Insight.Web.Constants;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using MongoDB.Driver;
|
||||
using MudBlazor;
|
||||
using System.Text.RegularExpressions;
|
||||
using SortDirection = MudBlazor.SortDirection;
|
||||
|
||||
namespace Insight.Web.Pages.Inventory.Hardware.Processors
|
||||
{
|
||||
[Route(Navigation.Inventory.Hardware.Processors.Index)]
|
||||
public partial class Index
|
||||
{
|
||||
[Inject] private IMongoDatabase Database { get; init; } = default!;
|
||||
[Inject] private ISnackbar Snackbar { get; init; } = default!;
|
||||
|
||||
private TableContainer<IndexViewModel>? Container { get; set; }
|
||||
private string Title { get; set; } = Global.Name;
|
||||
private List<BreadcrumbItem> Breadcrumbs { get; } = new();
|
||||
private string? Search { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Title = $"Inventory » Processors|Insight";
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Home", href: Navigation.Home));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Inventory", href: "#", true));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Processors", href: "#", true));
|
||||
}
|
||||
|
||||
private async Task<TableData<IndexViewModel>> LoadDataAsync(TableState state)
|
||||
{
|
||||
try
|
||||
{
|
||||
var filter = Builders<HostProcessorEntity>.Filter.Empty;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Search) is false)
|
||||
{
|
||||
var regex = new BsonRegularExpression(new Regex(Search, RegexOptions.IgnoreCase));
|
||||
filter &= Builders<HostProcessorEntity>.Filter.Regex(x => x.Company, regex) |
|
||||
Builders<HostProcessorEntity>.Filter.Regex(x => x.Name, regex);
|
||||
}
|
||||
|
||||
var query = Database.HostProcessor()
|
||||
.Aggregate()
|
||||
.Match(filter)
|
||||
.Lookup("host", "_host", "_id", "hosts")
|
||||
.Match(new BsonDocument("hosts", new BsonDocument
|
||||
{
|
||||
{ "$exists", true },
|
||||
{ "$ne", new BsonArray() }
|
||||
}))
|
||||
.Group(new BsonDocument
|
||||
{
|
||||
{ "_id", new BsonDocument("name", "$name") },
|
||||
{ "company", new BsonDocument("$first", "$company") },
|
||||
{ "name", new BsonDocument("$first", "$name") },
|
||||
{ "hosts", new BsonDocument("$addToSet", "$hosts") },
|
||||
})
|
||||
.Project(new BsonDocument
|
||||
{
|
||||
{ "_id", 0 },
|
||||
{ "company", 1 },
|
||||
{ "name", 1 },
|
||||
{ "hosts", 1 },
|
||||
{ "hosts_size", new BsonDocument("$size", "$hosts") },
|
||||
})
|
||||
.Sort(state.SortDirection switch
|
||||
{
|
||||
SortDirection.Ascending => state.SortLabel switch
|
||||
{
|
||||
"Company" => Builders<BsonDocument>.Sort.Ascending("company"),
|
||||
"Name" => Builders<BsonDocument>.Sort.Ascending("name"),
|
||||
"Hosts" => Builders<BsonDocument>.Sort.Ascending("hosts_size"),
|
||||
_ => null
|
||||
},
|
||||
SortDirection.Descending => state.SortLabel switch
|
||||
{
|
||||
"Company" => Builders<BsonDocument>.Sort.Descending("company"),
|
||||
"Name" => Builders<BsonDocument>.Sort.Descending("name"),
|
||||
"Hosts" => Builders<BsonDocument>.Sort.Descending("hosts_size"),
|
||||
_ => null
|
||||
},
|
||||
_ => Builders<BsonDocument>.Sort.Descending("hosts_size")
|
||||
});
|
||||
|
||||
var countResult = await query.Count().FirstOrDefaultAsync(default);
|
||||
var itemResult = await query.Skip(state.Page * state.PageSize).Limit(state.PageSize).ToListAsync(default);
|
||||
|
||||
return new TableData<IndexViewModel>()
|
||||
{
|
||||
TotalItems = countResult is null ? 0 : (int)countResult.Count,
|
||||
Items = itemResult.Select(x => BsonSerializer.Deserialize<IndexViewModel>(x))
|
||||
};
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Notification.Error(Snackbar);
|
||||
return new TableData<IndexViewModel>();
|
||||
}
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
public class IndexViewModel
|
||||
{
|
||||
[BsonElement("company")]
|
||||
public string? Company { get; set; }
|
||||
|
||||
[BsonElement("name")]
|
||||
public string? Name { get; set; }
|
||||
|
||||
[BsonElement("hosts_size")]
|
||||
public int? Hosts { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
@inherits ComponentBase
|
||||
|
||||
<TableContainer T="ViewModel"
|
||||
@ref="Container"
|
||||
@bind-Search="Search"
|
||||
Title="@Title"
|
||||
Breadcrumbs="@Breadcrumbs"
|
||||
Data="LoadDataAsync">
|
||||
<Header>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Customer" T="ViewModel">
|
||||
Customer
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Host" T="ViewModel">
|
||||
Host
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Driver" T="ViewModel">
|
||||
Driver (Version)
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Date" T="ViewModel">
|
||||
Driver (Date)
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
</Header>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="Customer">
|
||||
<MudLink Href="@Navigation.Management.Customers.DetailsHref(@context?.Customers?.Id)" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Customers?.Name
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Host">
|
||||
<MudLink Href="@Navigation.Management.Hosts.DetailsHref(@context?.Hosts?.Id)" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Hosts?.Name
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Driver">
|
||||
@context?.Driver
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Date">
|
||||
@context?.Date
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</TableContainer>
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
using Insight.Infrastructure;
|
||||
using Insight.Infrastructure.Entities;
|
||||
using Insight.Web.Components.Containers;
|
||||
using Insight.Web.Constants;
|
||||
using Insight.Web.Extensions;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using MongoDB.Driver;
|
||||
using MongoDB.Driver.Linq;
|
||||
using MudBlazor;
|
||||
using System.Text.RegularExpressions;
|
||||
using SortDirection = MudBlazor.SortDirection;
|
||||
|
||||
namespace Insight.Web.Pages.Inventory.Hardware.Videocards
|
||||
{
|
||||
[Route(Navigation.Inventory.Hardware.Videocards.Hosts)]
|
||||
public partial class Hosts
|
||||
{
|
||||
[Parameter] public string? VideocardName { get; set; }
|
||||
|
||||
[Inject] private IMongoDatabase Database { get; init; } = default!;
|
||||
[Inject] private ISnackbar Snackbar { get; init; } = default!;
|
||||
[Inject] private NavigationManager NavigationManager { get; init; } = default!;
|
||||
|
||||
private TableContainer<ViewModel>? Container { get; set; }
|
||||
private string Title { get; set; } = Global.Name;
|
||||
private List<BreadcrumbItem> Breadcrumbs { get; } = new();
|
||||
private string? Search { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Home", href: Navigation.Home));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Inventory", href: "#", true));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Videocards", href: Navigation.Inventory.Hardware.Videocards.Index));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(VideocardName))
|
||||
{
|
||||
Notification.Error(Snackbar, "Not Found");
|
||||
NavigationManager.NavigateTo(Navigation.Inventory.Hardware.Videocards.Index);
|
||||
return;
|
||||
}
|
||||
|
||||
Title = $"Inventory » Videocards » {VideocardName} » Hosts|Insight";
|
||||
Breadcrumbs.Add(new BreadcrumbItem(VideocardName, href: "#", true));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Hosts", href: "#", true));
|
||||
}
|
||||
|
||||
private async Task<TableData<ViewModel>> LoadDataAsync(TableState state)
|
||||
{
|
||||
try
|
||||
{
|
||||
var filter = Builders<BsonDocument>.Filter.Empty;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Search) is false)
|
||||
{
|
||||
var regex = new BsonRegularExpression(new Regex(Search, RegexOptions.IgnoreCase));
|
||||
filter &= Builders<BsonDocument>.Filter.Regex("customer.name", regex) |
|
||||
Builders<BsonDocument>.Filter.Regex("host.name", regex) |
|
||||
Builders<BsonDocument>.Filter.Regex("driver", regex) |
|
||||
Builders<BsonDocument>.Filter.Regex("date", regex);
|
||||
}
|
||||
|
||||
var query = Database.HostVideocard()
|
||||
.Aggregate()
|
||||
.Match(Builders<HostVideocardEntity>.Filter.Regex(p => p.Name, new BsonRegularExpression(new Regex(VideocardName.Escape(), RegexOptions.IgnoreCase))))
|
||||
.Lookup("host", "_host", "_id", "hosts")
|
||||
.Match(new BsonDocument("hosts", new BsonDocument
|
||||
{
|
||||
{ "$exists", true },
|
||||
{ "$ne", new BsonArray() }
|
||||
}))
|
||||
.Lookup("customer", "hosts._customer", "_id", "customers")
|
||||
.Project(new BsonDocument
|
||||
{
|
||||
{ "_id", 0 },
|
||||
{ "driver", 1 },
|
||||
{ "date", 1 },
|
||||
{ "host", new BsonDocument("$first", "$hosts") },
|
||||
{ "customer", new BsonDocument("$first", "$customers") },
|
||||
})
|
||||
.Match(filter)
|
||||
.Sort(state.SortDirection switch
|
||||
{
|
||||
SortDirection.Ascending => state.SortLabel switch
|
||||
{
|
||||
"Customer" => Builders<BsonDocument>.Sort.Ascending("customer.name"),
|
||||
"Host" => Builders<BsonDocument>.Sort.Ascending("host.name"),
|
||||
"Driver" => Builders<BsonDocument>.Sort.Ascending("driver"),
|
||||
"Date" => Builders<BsonDocument>.Sort.Ascending("date"),
|
||||
_ => null
|
||||
},
|
||||
SortDirection.Descending => state.SortLabel switch
|
||||
{
|
||||
"Customer" => Builders<BsonDocument>.Sort.Descending("customer.name"),
|
||||
"Host" => Builders<BsonDocument>.Sort.Descending("host.name"),
|
||||
"Driver" => Builders<BsonDocument>.Sort.Descending("driver"),
|
||||
"Date" => Builders<BsonDocument>.Sort.Descending("date"),
|
||||
_ => null
|
||||
},
|
||||
_ => Builders<BsonDocument>.Sort.Ascending("customer.name")
|
||||
});
|
||||
|
||||
var countResult = await query.Count().FirstOrDefaultAsync(default);
|
||||
var itemResult = await query.Skip(state.Page * state.PageSize).Limit(state.PageSize).ToListAsync(default);
|
||||
|
||||
return new TableData<ViewModel>()
|
||||
{
|
||||
TotalItems = countResult is null ? 0 : (int)countResult.Count,
|
||||
Items = itemResult.Select(x => BsonSerializer.Deserialize<ViewModel>(x))
|
||||
};
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Notification.Error(Snackbar);
|
||||
return new TableData<ViewModel>();
|
||||
}
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
public class ViewModel
|
||||
{
|
||||
[BsonElement("driver")]
|
||||
public string? Driver { get; set; }
|
||||
|
||||
[BsonElement("date")]
|
||||
public DateTime? Date { get; set; }
|
||||
|
||||
[BsonElement("host")]
|
||||
public HostEntity? Hosts { get; set; }
|
||||
|
||||
[BsonElement("customer")]
|
||||
public CustomerEntity? Customers { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
@inherits ComponentBase
|
||||
|
||||
<TableContainer T="ViewModel"
|
||||
@ref="Container"
|
||||
@bind-Search="Search"
|
||||
Title="@Title"
|
||||
Breadcrumbs="@Breadcrumbs"
|
||||
Data="LoadDataAsync">
|
||||
<Header>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Name" T="ViewModel">
|
||||
Name
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Hosts" T="ViewModel">
|
||||
Hosts
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
</Header>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="Name">
|
||||
@context?.Name
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Hosts">
|
||||
<MudLink Href="@Navigation.Inventory.Hardware.Videocards.HostsHref(context?.Name)" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Hosts
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</TableContainer>
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
using Insight.Infrastructure;
|
||||
using Insight.Infrastructure.Entities;
|
||||
using Insight.Web.Components.Containers;
|
||||
using Insight.Web.Constants;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using MongoDB.Driver;
|
||||
using MudBlazor;
|
||||
using System.Text.RegularExpressions;
|
||||
using SortDirection = MudBlazor.SortDirection;
|
||||
|
||||
namespace Insight.Web.Pages.Inventory.Hardware.Videocards
|
||||
{
|
||||
[Route(Navigation.Inventory.Hardware.Videocards.Index)]
|
||||
public partial class Index
|
||||
{
|
||||
[Inject] private IMongoDatabase Database { get; init; } = default!;
|
||||
[Inject] private ISnackbar Snackbar { get; init; } = default!;
|
||||
|
||||
private TableContainer<ViewModel>? Container { get; set; }
|
||||
private string Title { get; set; } = Global.Name;
|
||||
private List<BreadcrumbItem> Breadcrumbs { get; } = new();
|
||||
private string? Search { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Title = $"Inventory » Videocards|Insight";
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Home", href: Navigation.Home));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Inventory", href: "#", true));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Videocards", href: "#", true));
|
||||
}
|
||||
|
||||
private async Task<TableData<ViewModel>> LoadDataAsync(TableState state)
|
||||
{
|
||||
try
|
||||
{
|
||||
var filter = Builders<HostVideocardEntity>.Filter.Empty;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Search) is false)
|
||||
{
|
||||
var regex = new BsonRegularExpression(new Regex(Search, RegexOptions.IgnoreCase));
|
||||
filter &= Builders<HostVideocardEntity>.Filter.Regex(x => x.Company, regex) |
|
||||
Builders<HostVideocardEntity>.Filter.Regex(x => x.Name, regex);
|
||||
}
|
||||
|
||||
var query = Database.HostVideocard()
|
||||
.Aggregate()
|
||||
.Match(filter)
|
||||
.Lookup("host", "_host", "_id", "hosts")
|
||||
.Match(new BsonDocument("hosts", new BsonDocument
|
||||
{
|
||||
{ "$exists", true },
|
||||
{ "$ne", new BsonArray() }
|
||||
}))
|
||||
.Group(new BsonDocument
|
||||
{
|
||||
{ "_id", new BsonDocument("name", "$name") },
|
||||
{ "name", new BsonDocument("$first", "$name") },
|
||||
{ "hosts", new BsonDocument("$addToSet", "$hosts") },
|
||||
})
|
||||
.Project(new BsonDocument
|
||||
{
|
||||
{ "_id", 0 },
|
||||
{ "name", 1 },
|
||||
{ "hosts", 1 },
|
||||
{ "hosts_size", new BsonDocument("$size", "$hosts") },
|
||||
})
|
||||
.Sort(state.SortDirection switch
|
||||
{
|
||||
SortDirection.Ascending => state.SortLabel switch
|
||||
{
|
||||
"Name" => Builders<BsonDocument>.Sort.Ascending("name"),
|
||||
"Hosts" => Builders<BsonDocument>.Sort.Ascending("hosts_size"),
|
||||
_ => null
|
||||
},
|
||||
SortDirection.Descending => state.SortLabel switch
|
||||
{
|
||||
"Name" => Builders<BsonDocument>.Sort.Descending("name"),
|
||||
"Hosts" => Builders<BsonDocument>.Sort.Descending("hosts_size"),
|
||||
_ => null
|
||||
},
|
||||
_ => Builders<BsonDocument>.Sort.Descending("hosts_size")
|
||||
});
|
||||
|
||||
var countResult = await query.Count().FirstOrDefaultAsync(default);
|
||||
var itemResult = await query.Skip(state.Page * state.PageSize).Limit(state.PageSize).ToListAsync(default);
|
||||
|
||||
return new TableData<ViewModel>()
|
||||
{
|
||||
TotalItems = countResult is null ? 0 : (int)countResult.Count,
|
||||
Items = itemResult.Select(x => BsonSerializer.Deserialize<ViewModel>(x))
|
||||
};
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Notification.Error(Snackbar);
|
||||
return new TableData<ViewModel>();
|
||||
}
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
public class ViewModel
|
||||
{
|
||||
[BsonElement("name")]
|
||||
public string? Name { get; set; }
|
||||
|
||||
[BsonElement("hosts_size")]
|
||||
public int? Hosts { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
@inherits ComponentBase
|
||||
|
||||
<TableContainer T="ViewModel"
|
||||
@ref="Container"
|
||||
@bind-Search="Search"
|
||||
Title="@Title"
|
||||
Breadcrumbs="@Breadcrumbs"
|
||||
Data="LoadDataAsync">
|
||||
<Header>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Customer" T="ViewModel">
|
||||
Customer
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Host" T="ViewModel">
|
||||
Host
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Interface" T="ViewModel">
|
||||
Interface
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Mask" T="ViewModel">
|
||||
Mask
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
</Header>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="Customer">
|
||||
<MudLink Href="@Navigation.Management.Customers.DetailsHref(@context?.Customers?.Id)" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Customers?.Name
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Host">
|
||||
<MudLink Href="@Navigation.Management.Hosts.DetailsHref(@context?.Hosts?.Id)" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Hosts?.Name
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Interface">
|
||||
<MudLink Href="@Navigation.Management.Hosts.Network.Interfaces.DetailsHref(@context?.Hosts?.Id, @context?.Interface?.Id.ToString())" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Interface?.Name
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Mask">
|
||||
@context?.Mask
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</TableContainer>
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
using Insight.Infrastructure;
|
||||
using Insight.Infrastructure.Entities;
|
||||
using Insight.Web.Components.Containers;
|
||||
using Insight.Web.Constants;
|
||||
using Insight.Web.Extensions;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using MongoDB.Driver;
|
||||
using MongoDB.Driver.Linq;
|
||||
using MudBlazor;
|
||||
using System.Text.RegularExpressions;
|
||||
using SortDirection = MudBlazor.SortDirection;
|
||||
|
||||
namespace Insight.Web.Pages.Inventory.Network.Addresses;
|
||||
|
||||
[Route(Navigation.Inventory.Network.Addresses.Hosts)]
|
||||
public partial class Hosts
|
||||
{
|
||||
[Parameter] public string? Address { get; set; }
|
||||
|
||||
[Inject] private IMongoDatabase Database { get; init; } = default!;
|
||||
[Inject] private ISnackbar Snackbar { get; init; } = default!;
|
||||
[Inject] private NavigationManager NavigationManager { get; init; } = default!;
|
||||
|
||||
private TableContainer<ViewModel>? Container { get; set; }
|
||||
private string Title { get; set; } = Global.Name;
|
||||
private List<BreadcrumbItem> Breadcrumbs { get; } = new();
|
||||
private string? Search { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Address = Address?.UriEscape(true);
|
||||
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Home", href: Navigation.Home));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Inventory", href: "#", true));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Addresses", href: Navigation.Inventory.Network.Addresses.Index));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Address))
|
||||
{
|
||||
Notification.Error(Snackbar, "Not Found");
|
||||
NavigationManager.NavigateTo(Navigation.Inventory.Network.Addresses.Index);
|
||||
return;
|
||||
}
|
||||
|
||||
Title = $"Inventory » Address » {Address} » Hosts|Insight";
|
||||
Breadcrumbs.Add(new BreadcrumbItem(Address, href: "#", true));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Hosts", href: "#", true));
|
||||
}
|
||||
|
||||
private async Task<TableData<ViewModel>> LoadDataAsync(TableState state)
|
||||
{
|
||||
try
|
||||
{
|
||||
var filter = Builders<BsonDocument>.Filter.Empty;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Search) is false)
|
||||
{
|
||||
var regex = new BsonRegularExpression(new Regex(Search, RegexOptions.IgnoreCase));
|
||||
filter &= Builders<BsonDocument>.Filter.Regex("customer.name", regex) |
|
||||
Builders<BsonDocument>.Filter.Regex("host.name", regex) |
|
||||
Builders<BsonDocument>.Filter.Regex("interface", regex) |
|
||||
Builders<BsonDocument>.Filter.Regex("mask", regex);
|
||||
}
|
||||
|
||||
var query = Database.HostInterfaceAddress()
|
||||
.Aggregate()
|
||||
.Match(Builders<HostInterfaceAddressEntity>.Filter.Eq(p => p.Address, Address))
|
||||
.Lookup("host", "_host", "_id", "hosts")
|
||||
.Match(new BsonDocument("hosts", new BsonDocument
|
||||
{
|
||||
{ "$exists", true },
|
||||
{ "$ne", new BsonArray() }
|
||||
}))
|
||||
.Lookup("customer", "hosts._customer", "_id", "customers")
|
||||
.Lookup("host_if", "_interface", "_id", "interfaces")
|
||||
.Project(new BsonDocument
|
||||
{
|
||||
{ "_id", 0 },
|
||||
{ "mask", 1 },
|
||||
{ "interface", new BsonDocument("$first", "$interfaces") },
|
||||
{ "host", new BsonDocument("$first", "$hosts") },
|
||||
{ "customer", new BsonDocument("$first", "$customers") },
|
||||
})
|
||||
.Match(filter)
|
||||
.Sort(state.SortDirection switch
|
||||
{
|
||||
SortDirection.Ascending => state.SortLabel switch
|
||||
{
|
||||
"Customer" => Builders<BsonDocument>.Sort.Ascending("customer.name"),
|
||||
"Host" => Builders<BsonDocument>.Sort.Ascending("host.name"),
|
||||
"Mask" => Builders<BsonDocument>.Sort.Ascending("mask"),
|
||||
"Interface" => Builders<BsonDocument>.Sort.Ascending("interface.name"),
|
||||
_ => null
|
||||
},
|
||||
SortDirection.Descending => state.SortLabel switch
|
||||
{
|
||||
"Customer" => Builders<BsonDocument>.Sort.Descending("customer.name"),
|
||||
"Host" => Builders<BsonDocument>.Sort.Descending("host.name"),
|
||||
"Mask" => Builders<BsonDocument>.Sort.Descending("mask"),
|
||||
"Interface" => Builders<BsonDocument>.Sort.Descending("interface.name"),
|
||||
_ => null
|
||||
},
|
||||
_ => Builders<BsonDocument>.Sort.Ascending("customer.name")
|
||||
});
|
||||
|
||||
var countResult = await query.Count().FirstOrDefaultAsync(default);
|
||||
var itemResult = await query.Skip(state.Page * state.PageSize).Limit(state.PageSize).ToListAsync(default);
|
||||
|
||||
return new TableData<ViewModel>()
|
||||
{
|
||||
TotalItems = countResult is null ? 0 : (int)countResult.Count,
|
||||
Items = itemResult.Select(x => BsonSerializer.Deserialize<ViewModel>(x))
|
||||
};
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Notification.Error(Snackbar);
|
||||
return new TableData<ViewModel>();
|
||||
}
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
public class ViewModel
|
||||
{
|
||||
[BsonElement("mask")]
|
||||
public string? Mask { get; set; }
|
||||
|
||||
[BsonElement("interface")]
|
||||
public HostInterfaceEntity? Interface { get; set; }
|
||||
|
||||
[BsonElement("host")]
|
||||
public HostEntity? Hosts { get; set; }
|
||||
|
||||
[BsonElement("customer")]
|
||||
public CustomerEntity? Customers { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
@inherits ComponentBase
|
||||
@using Insight.Web.Extensions
|
||||
|
||||
<TableContainer T="ViewModel"
|
||||
@ref="Container"
|
||||
@bind-Search="Search"
|
||||
Title="@Title"
|
||||
Breadcrumbs="@Breadcrumbs"
|
||||
Data="LoadDataAsync">
|
||||
<Header>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Address" T="ViewModel">
|
||||
Address
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Hosts" T="ViewModel">
|
||||
Hosts
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
</Header>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="Address">
|
||||
@context?.Address
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Hosts">
|
||||
<MudLink Href="@Navigation.Inventory.Network.Addresses.HostsHref(context?.Address?.UriEscape())" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Hosts
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</TableContainer>
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
using Insight.Infrastructure;
|
||||
using Insight.Infrastructure.Entities;
|
||||
using Insight.Web.Components.Containers;
|
||||
using Insight.Web.Constants;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using MongoDB.Driver;
|
||||
using MudBlazor;
|
||||
using System.Text.RegularExpressions;
|
||||
using SortDirection = MudBlazor.SortDirection;
|
||||
|
||||
namespace Insight.Web.Pages.Inventory.Network.Addresses;
|
||||
|
||||
[Route(Navigation.Inventory.Network.Addresses.Index)]
|
||||
public partial class Index
|
||||
{
|
||||
[Inject] private IMongoDatabase Database { get; init; } = default!;
|
||||
[Inject] private ISnackbar Snackbar { get; init; } = default!;
|
||||
|
||||
private TableContainer<ViewModel>? Container { get; set; }
|
||||
private string Title { get; set; } = Global.Name;
|
||||
private List<BreadcrumbItem> Breadcrumbs { get; } = new();
|
||||
private string? Search { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Title = $"Inventory » Addresses|Insight";
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Home", href: Navigation.Home));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Inventory", href: "#", true));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Addresses", href: "#", true));
|
||||
}
|
||||
|
||||
private async Task<TableData<ViewModel>> LoadDataAsync(TableState state)
|
||||
{
|
||||
try
|
||||
{
|
||||
var filter = Builders<HostInterfaceAddressEntity>.Filter.Empty;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Search) is false)
|
||||
{
|
||||
var regex = new BsonRegularExpression(new Regex(Search, RegexOptions.IgnoreCase));
|
||||
filter &= Builders<HostInterfaceAddressEntity>.Filter.Regex(x => x.Address, regex);
|
||||
}
|
||||
|
||||
var query = Database.HostInterfaceAddress()
|
||||
.Aggregate()
|
||||
.Match(filter)
|
||||
.Lookup("host", "_host", "_id", "hosts")
|
||||
.Match(new BsonDocument("hosts", new BsonDocument
|
||||
{
|
||||
{ "$exists", true },
|
||||
{ "$ne", new BsonArray() }
|
||||
}))
|
||||
.Group(new BsonDocument
|
||||
{
|
||||
{ "_id", new BsonDocument("address", "$address") },
|
||||
{ "address", new BsonDocument("$first", "$address") },
|
||||
{ "hosts", new BsonDocument("$addToSet", "$hosts") },
|
||||
})
|
||||
.Project(new BsonDocument
|
||||
{
|
||||
{ "_id", 0 },
|
||||
{ "address", 1 },
|
||||
{ "hosts", 1 },
|
||||
{ "hosts_size", new BsonDocument("$size", "$hosts") },
|
||||
})
|
||||
.Sort(state.SortDirection switch
|
||||
{
|
||||
SortDirection.Ascending => state.SortLabel switch
|
||||
{
|
||||
"Address" => Builders<BsonDocument>.Sort.Ascending("address"),
|
||||
"Hosts" => Builders<BsonDocument>.Sort.Ascending("hosts_size"),
|
||||
_ => null
|
||||
},
|
||||
SortDirection.Descending => state.SortLabel switch
|
||||
{
|
||||
"Address" => Builders<BsonDocument>.Sort.Descending("address"),
|
||||
"Hosts" => Builders<BsonDocument>.Sort.Descending("hosts_size"),
|
||||
_ => null
|
||||
},
|
||||
_ => Builders<BsonDocument>.Sort.Descending("hosts_size")
|
||||
});
|
||||
|
||||
var countResult = await query.Count().FirstOrDefaultAsync(default);
|
||||
var itemResult = await query.Skip(state.Page * state.PageSize).Limit(state.PageSize).ToListAsync(default);
|
||||
|
||||
return new TableData<ViewModel>()
|
||||
{
|
||||
TotalItems = countResult is null ? 0 : (int)countResult.Count,
|
||||
Items = itemResult.Select(x => BsonSerializer.Deserialize<ViewModel>(x))
|
||||
};
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Notification.Error(Snackbar);
|
||||
return new TableData<ViewModel>();
|
||||
}
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
public class ViewModel
|
||||
{
|
||||
[BsonElement("address")]
|
||||
public string? Address { get; set; }
|
||||
|
||||
[BsonElement("hosts_size")]
|
||||
public int? Hosts { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
@inherits ComponentBase
|
||||
|
||||
|
||||
<TableContainer T="ViewModel"
|
||||
@ref="Container"
|
||||
@bind-Search="Search"
|
||||
Title="@Title"
|
||||
Breadcrumbs="@Breadcrumbs"
|
||||
Data="LoadDataAsync">
|
||||
<Header>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Customer" T="ViewModel">
|
||||
Customer
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Host" T="ViewModel">
|
||||
Host
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Interface" T="ViewModel">
|
||||
Interface
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
</Header>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="Customer">
|
||||
<MudLink Href="@Navigation.Management.Customers.DetailsHref(@context?.Customers?.Id)" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Customers?.Name
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Host">
|
||||
<MudLink Href="@Navigation.Management.Hosts.DetailsHref(@context?.Hosts?.Id)" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Hosts?.Name
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Interface">
|
||||
<MudLink Href="@Navigation.Management.Hosts.Network.Interfaces.DetailsHref(@context?.Hosts?.Id, @context?.Interface?.Id.ToString())" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Interface?.Name
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</TableContainer>
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
using Insight.Infrastructure;
|
||||
using Insight.Infrastructure.Entities;
|
||||
using Insight.Web.Components.Containers;
|
||||
using Insight.Web.Constants;
|
||||
using Insight.Web.Extensions;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using MongoDB.Driver;
|
||||
using MongoDB.Driver.Linq;
|
||||
using MudBlazor;
|
||||
using System.Text.RegularExpressions;
|
||||
using SortDirection = MudBlazor.SortDirection;
|
||||
|
||||
namespace Insight.Web.Pages.Inventory.Network.Gateways
|
||||
{
|
||||
[Route(Navigation.Inventory.Network.Gateways.Hosts)]
|
||||
public partial class Hosts
|
||||
{
|
||||
[Parameter] public string? GatewayAddress { get; set; }
|
||||
|
||||
[Inject] private IMongoDatabase Database { get; init; } = default!;
|
||||
[Inject] private ISnackbar Snackbar { get; init; } = default!;
|
||||
[Inject] private NavigationManager NavigationManager { get; init; } = default!;
|
||||
|
||||
private TableContainer<ViewModel>? Container { get; set; }
|
||||
private string Title { get; set; } = Global.Name;
|
||||
private List<BreadcrumbItem> Breadcrumbs { get; } = new();
|
||||
private string? Search { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
GatewayAddress = GatewayAddress?.UriEscape(true);
|
||||
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Home", href: Navigation.Home));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Inventory", href: "#", true));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Gateways", href: Navigation.Inventory.Network.Gateways.Index));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(GatewayAddress))
|
||||
{
|
||||
Notification.Error(Snackbar, "Not Found");
|
||||
NavigationManager.NavigateTo(Navigation.Inventory.Network.Gateways.Index);
|
||||
return;
|
||||
}
|
||||
|
||||
Title = $"Inventory » Gateway » {GatewayAddress} » Hosts|Insight";
|
||||
Breadcrumbs.Add(new BreadcrumbItem(GatewayAddress, href: "#", true));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Hosts", href: "#", true));
|
||||
}
|
||||
|
||||
private async Task<TableData<ViewModel>> LoadDataAsync(TableState state)
|
||||
{
|
||||
try
|
||||
{
|
||||
var filter = Builders<BsonDocument>.Filter.Empty;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Search) is false)
|
||||
{
|
||||
var regex = new BsonRegularExpression(new Regex(Search, RegexOptions.IgnoreCase));
|
||||
filter &= Builders<BsonDocument>.Filter.Regex("customer.name", regex) |
|
||||
Builders<BsonDocument>.Filter.Regex("host.name", regex) |
|
||||
Builders<BsonDocument>.Filter.Regex("interface", regex);
|
||||
}
|
||||
|
||||
var query = Database.HostInterfaceGateway()
|
||||
.Aggregate()
|
||||
.Match(Builders<HostInterfaceGatewayEntity>.Filter.Eq(p => p.Address, GatewayAddress))
|
||||
.Lookup("host", "_host", "_id", "hosts")
|
||||
.Match(new BsonDocument("hosts", new BsonDocument
|
||||
{
|
||||
{ "$exists", true },
|
||||
{ "$ne", new BsonArray() }
|
||||
}))
|
||||
.Lookup("customer", "hosts._customer", "_id", "customers")
|
||||
.Lookup("host_if", "_interface", "_id", "interfaces")
|
||||
.Project(new BsonDocument
|
||||
{
|
||||
{ "_id", 0 },
|
||||
{ "interface", new BsonDocument("$first", "$interfaces") },
|
||||
{ "host", new BsonDocument("$first", "$hosts") },
|
||||
{ "customer", new BsonDocument("$first", "$customers") },
|
||||
})
|
||||
.Match(filter)
|
||||
.Sort(state.SortDirection switch
|
||||
{
|
||||
SortDirection.Ascending => state.SortLabel switch
|
||||
{
|
||||
"Customer" => Builders<BsonDocument>.Sort.Ascending("customer.name"),
|
||||
"Host" => Builders<BsonDocument>.Sort.Ascending("host.name"),
|
||||
"Interface" => Builders<BsonDocument>.Sort.Ascending("interface.name"),
|
||||
_ => null
|
||||
},
|
||||
SortDirection.Descending => state.SortLabel switch
|
||||
{
|
||||
"Customer" => Builders<BsonDocument>.Sort.Descending("customer.name"),
|
||||
"Host" => Builders<BsonDocument>.Sort.Descending("host.name"),
|
||||
"Interface" => Builders<BsonDocument>.Sort.Descending("interface.name"),
|
||||
_ => null
|
||||
},
|
||||
_ => Builders<BsonDocument>.Sort.Ascending("customer.name")
|
||||
});
|
||||
|
||||
var countResult = await query.Count().FirstOrDefaultAsync(default);
|
||||
var itemResult = await query.Skip(state.Page * state.PageSize).Limit(state.PageSize).ToListAsync(default);
|
||||
|
||||
return new TableData<ViewModel>()
|
||||
{
|
||||
TotalItems = countResult is null ? 0 : (int)countResult.Count,
|
||||
Items = itemResult.Select(x => BsonSerializer.Deserialize<ViewModel>(x))
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
|
||||
Notification.Error(Snackbar);
|
||||
return new TableData<ViewModel>();
|
||||
}
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
public class ViewModel
|
||||
{
|
||||
[BsonElement("interface")]
|
||||
public HostInterfaceEntity? Interface { get; set; }
|
||||
|
||||
[BsonElement("host")]
|
||||
public HostEntity? Hosts { get; set; }
|
||||
|
||||
[BsonElement("customer")]
|
||||
public CustomerEntity? Customers { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
@inherits ComponentBase
|
||||
@using Insight.Web.Extensions
|
||||
|
||||
<TableContainer T="ViewModel"
|
||||
@ref="Container"
|
||||
@bind-Search="Search"
|
||||
Title="@Title"
|
||||
Breadcrumbs="@Breadcrumbs"
|
||||
Data="LoadDataAsync">
|
||||
<Header>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Address" T="ViewModel">
|
||||
Address
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Hosts" T="ViewModel">
|
||||
Hosts
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
</Header>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="Address">
|
||||
@context?.Address
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Hosts">
|
||||
<MudLink Href="@Navigation.Inventory.Network.Gateways.HostsHref(context?.Address?.UriEscape())" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Hosts
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</TableContainer>
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
using Insight.Infrastructure;
|
||||
using Insight.Infrastructure.Entities;
|
||||
using Insight.Web.Components.Containers;
|
||||
using Insight.Web.Constants;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using MongoDB.Driver;
|
||||
using MudBlazor;
|
||||
using System.Text.RegularExpressions;
|
||||
using SortDirection = MudBlazor.SortDirection;
|
||||
|
||||
namespace Insight.Web.Pages.Inventory.Network.Gateways;
|
||||
|
||||
[Route(Navigation.Inventory.Network.Gateways.Index)]
|
||||
public partial class Index
|
||||
{
|
||||
[Inject] private IMongoDatabase Database { get; init; } = default!;
|
||||
[Inject] private ISnackbar Snackbar { get; init; } = default!;
|
||||
|
||||
private TableContainer<ViewModel>? Container { get; set; }
|
||||
private string Title { get; set; } = Global.Name;
|
||||
private List<BreadcrumbItem> Breadcrumbs { get; } = new();
|
||||
private string? Search { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Title = $"Inventory » Gateways|Insight";
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Home", href: Navigation.Home));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Inventory", href: "#", true));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Gateways", href: "#", true));
|
||||
}
|
||||
|
||||
private async Task<TableData<ViewModel>> LoadDataAsync(TableState state)
|
||||
{
|
||||
try
|
||||
{
|
||||
var filter = Builders<HostInterfaceGatewayEntity>.Filter.Empty;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Search) is false)
|
||||
{
|
||||
var regex = new BsonRegularExpression(new Regex(Search, RegexOptions.IgnoreCase));
|
||||
filter &= Builders<HostInterfaceGatewayEntity>.Filter.Regex(x => x.Address, regex);
|
||||
}
|
||||
|
||||
var query = Database.HostInterfaceGateway()
|
||||
.Aggregate()
|
||||
.Match(filter)
|
||||
.Lookup("host", "_host", "_id", "hosts")
|
||||
.Match(new BsonDocument("hosts", new BsonDocument
|
||||
{
|
||||
{ "$exists", true },
|
||||
{ "$ne", new BsonArray() }
|
||||
}))
|
||||
.Group(new BsonDocument
|
||||
{
|
||||
{ "_id", new BsonDocument("address", "$address") },
|
||||
{ "address", new BsonDocument("$first", "$address") },
|
||||
{ "hosts", new BsonDocument("$addToSet", "$hosts") },
|
||||
})
|
||||
.Project(new BsonDocument
|
||||
{
|
||||
{ "_id", 0 },
|
||||
{ "address", 1 },
|
||||
{ "hosts", 1 },
|
||||
{ "hosts_size", new BsonDocument("$size", "$hosts") },
|
||||
})
|
||||
.Sort(state.SortDirection switch
|
||||
{
|
||||
SortDirection.Ascending => state.SortLabel switch
|
||||
{
|
||||
"Address" => Builders<BsonDocument>.Sort.Ascending("address"),
|
||||
"Hosts" => Builders<BsonDocument>.Sort.Ascending("hosts_size"),
|
||||
_ => null
|
||||
},
|
||||
SortDirection.Descending => state.SortLabel switch
|
||||
{
|
||||
"Address" => Builders<BsonDocument>.Sort.Descending("address"),
|
||||
"Hosts" => Builders<BsonDocument>.Sort.Descending("hosts_size"),
|
||||
_ => null
|
||||
},
|
||||
_ => Builders<BsonDocument>.Sort.Descending("hosts_size")
|
||||
});
|
||||
|
||||
var countResult = await query.Count().FirstOrDefaultAsync(default);
|
||||
var itemResult = await query.Skip(state.Page * state.PageSize).Limit(state.PageSize).ToListAsync(default);
|
||||
|
||||
return new TableData<ViewModel>()
|
||||
{
|
||||
TotalItems = countResult is null ? 0 : (int)countResult.Count,
|
||||
Items = itemResult.Select(x => BsonSerializer.Deserialize<ViewModel>(x))
|
||||
};
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Notification.Error(Snackbar);
|
||||
return new TableData<ViewModel>();
|
||||
}
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
public class ViewModel
|
||||
{
|
||||
[BsonElement("address")]
|
||||
public string? Address { get; set; }
|
||||
|
||||
[BsonElement("hosts_size")]
|
||||
public int? Hosts { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
@inherits ComponentBase
|
||||
|
||||
<TableContainer T="ViewModel"
|
||||
@ref="Container"
|
||||
@bind-Search="Search"
|
||||
Title="@Title"
|
||||
Breadcrumbs="@Breadcrumbs"
|
||||
Data="LoadDataAsync">
|
||||
<Header>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Customer" T="ViewModel">
|
||||
Customer
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Host" T="ViewModel">
|
||||
Host
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Name" T="ViewModel">
|
||||
Name
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Description" T="ViewModel">
|
||||
Adapter
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Mac" T="ViewModel">
|
||||
Mac
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Suffix" T="ViewModel">
|
||||
Suffix
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Status" T="ViewModel">
|
||||
Status
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
</Header>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="Customer">
|
||||
<MudLink Href="@Navigation.Management.Customers.DetailsHref(@context?.Customers?.Id)" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Customers?.Name
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Host">
|
||||
<MudLink Href="@Navigation.Management.Hosts.DetailsHref(@context?.Hosts?.Id)" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Hosts?.Name
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Name">
|
||||
<MudLink Href="@Navigation.Management.Hosts.Network.Interfaces.DetailsHref(@context?.Hosts?.Id, context?.Id?.ToString())" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@{
|
||||
var value = context?.Name;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(value)) value = "N/A";
|
||||
|
||||
@value
|
||||
}
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Description">
|
||||
@context?.Description
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Mac">
|
||||
@context?.Mac
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Suffix">
|
||||
@context?.Suffix
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Status">
|
||||
@if (context?.Status is not null && Enum.TryParse<System.Net.NetworkInformation.OperationalStatus>(context.Status, true, out var operationalStatus))
|
||||
{
|
||||
var color = operationalStatus == System.Net.NetworkInformation.OperationalStatus.Up ? Color.Success : Color.Error;
|
||||
|
||||
<MudText Color="color" Typo="Typo.inherit">
|
||||
@context?.Status
|
||||
</MudText>
|
||||
}
|
||||
else
|
||||
{
|
||||
@("Unknown")
|
||||
}
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</TableContainer>
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
using Insight.Infrastructure;
|
||||
using Insight.Infrastructure.Entities;
|
||||
using Insight.Web.Components.Containers;
|
||||
using Insight.Web.Constants;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using MongoDB.Driver;
|
||||
using MudBlazor;
|
||||
using System.Text.RegularExpressions;
|
||||
using SortDirection = MudBlazor.SortDirection;
|
||||
|
||||
namespace Insight.Web.Pages.Inventory.Network.Interfaces;
|
||||
|
||||
[Route(Navigation.Inventory.Network.Interfaces.Index)]
|
||||
public partial class Index
|
||||
{
|
||||
[Inject] private IMongoDatabase Database { get; init; } = default!;
|
||||
[Inject] private ISnackbar Snackbar { get; init; } = default!;
|
||||
|
||||
private TableContainer<ViewModel>? Container { get; set; }
|
||||
private string Title { get; set; } = Global.Name;
|
||||
private List<BreadcrumbItem> Breadcrumbs { get; } = new();
|
||||
private string? Search { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Title = $"Inventory » Interfaces|Insight";
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Home", href: Navigation.Home));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Inventory", href: "#", true));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Interfaces", href: "#", true));
|
||||
}
|
||||
|
||||
private async Task<TableData<ViewModel>> LoadDataAsync(TableState state)
|
||||
{
|
||||
try
|
||||
{
|
||||
var search = Builders<BsonDocument>.Filter.Empty;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Search) is false)
|
||||
{
|
||||
var regex = new BsonRegularExpression(new Regex(Search, RegexOptions.IgnoreCase));
|
||||
search &= Builders<BsonDocument>.Filter.Regex("host.name", regex) |
|
||||
Builders<BsonDocument>.Filter.Regex("customer.name", regex) |
|
||||
Builders<BsonDocument>.Filter.Regex("name", regex) |
|
||||
Builders<BsonDocument>.Filter.Regex("type", regex) |
|
||||
Builders<BsonDocument>.Filter.Regex("filesystem", regex);
|
||||
}
|
||||
|
||||
var query = Database.HostInterface()
|
||||
.Aggregate()
|
||||
.Lookup("host", "_host", "_id", "hosts")
|
||||
.Match(new BsonDocument("hosts", new BsonDocument
|
||||
{
|
||||
{ "$exists", true },
|
||||
{ "$ne", new BsonArray() }
|
||||
}))
|
||||
.AppendStage<BsonDocument>(new BsonDocument("$addFields", new BsonDocument
|
||||
{
|
||||
{ "host", new BsonDocument("$first", "$hosts") }
|
||||
}))
|
||||
.AppendStage<BsonDocument>(new BsonDocument("$lookup", new BsonDocument
|
||||
{
|
||||
{ "from", "customer" },
|
||||
{ "localField", "host._customer" },
|
||||
{ "foreignField", "_id" },
|
||||
{ "as", "customers" }
|
||||
}))
|
||||
.AppendStage<BsonDocument>(new BsonDocument("$addFields", new BsonDocument
|
||||
{
|
||||
{ "customer", new BsonDocument("$first", "$customers") }
|
||||
}))
|
||||
.Match(search)
|
||||
.Sort(state.SortDirection switch
|
||||
{
|
||||
SortDirection.Ascending => state.SortLabel switch
|
||||
{
|
||||
"Host" => Builders<BsonDocument>.Sort.Ascending("host.name"),
|
||||
"Customer" => Builders<BsonDocument>.Sort.Ascending("customer.name"),
|
||||
"Name" => Builders<BsonDocument>.Sort.Ascending("name"),
|
||||
"Description" => Builders<BsonDocument>.Sort.Ascending("description"),
|
||||
"Mac" => Builders<BsonDocument>.Sort.Ascending("mac"),
|
||||
"Suffix" => Builders<BsonDocument>.Sort.Ascending("suffix"),
|
||||
"Status" => Builders<BsonDocument>.Sort.Ascending("status"),
|
||||
_ => null
|
||||
},
|
||||
SortDirection.Descending => state.SortLabel switch
|
||||
{
|
||||
"Host" => Builders<BsonDocument>.Sort.Descending("host.name"),
|
||||
"Customer" => Builders<BsonDocument>.Sort.Descending("customer.name"),
|
||||
"Name" => Builders<BsonDocument>.Sort.Descending("name"),
|
||||
"Description" => Builders<BsonDocument>.Sort.Descending("description"),
|
||||
"Mac" => Builders<BsonDocument>.Sort.Descending("mac"),
|
||||
"Suffix" => Builders<BsonDocument>.Sort.Descending("suffix"),
|
||||
"Status" => Builders<BsonDocument>.Sort.Descending("status"),
|
||||
_ => null
|
||||
},
|
||||
_ => Builders<BsonDocument>.Sort.Ascending("customer.name")
|
||||
});
|
||||
|
||||
var countResult = await query.Count().FirstOrDefaultAsync(default);
|
||||
var itemResult = await query.Skip(state.Page * state.PageSize).Limit(state.PageSize).ToListAsync(default);
|
||||
|
||||
return new TableData<ViewModel>()
|
||||
{
|
||||
TotalItems = countResult is null ? 0 : (int)countResult.Count,
|
||||
Items = itemResult.Select(x => BsonSerializer.Deserialize<ViewModel>(x))
|
||||
};
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Notification.Error(Snackbar);
|
||||
return new TableData<ViewModel>();
|
||||
}
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
public class ViewModel
|
||||
{
|
||||
[BsonId]
|
||||
public ObjectId? Id { get; set; }
|
||||
|
||||
[BsonElement("host")]
|
||||
public HostEntity? Hosts { get; set; }
|
||||
|
||||
[BsonElement("customer")]
|
||||
public CustomerEntity? Customers { get; set; }
|
||||
|
||||
[BsonElement("name")]
|
||||
public string? Name { get; set; }
|
||||
|
||||
[BsonElement("description")]
|
||||
public string? Description { get; set; }
|
||||
|
||||
[BsonElement("mac")]
|
||||
public string? Mac { get; set; }
|
||||
|
||||
[BsonElement("suffix")]
|
||||
public string? Suffix { get; set; }
|
||||
|
||||
[BsonElement("status")]
|
||||
public string? Status { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
@inherits ComponentBase
|
||||
@using Insight.Web.Extensions
|
||||
|
||||
<TableContainer T="ViewModel"
|
||||
@ref="Container"
|
||||
@bind-Search="Search"
|
||||
Title="@Title"
|
||||
Breadcrumbs="@Breadcrumbs"
|
||||
Data="LoadDataAsync">
|
||||
<Header>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Customer" T="ViewModel">
|
||||
Customer
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Host" T="ViewModel">
|
||||
Host
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Interface" T="ViewModel">
|
||||
Interface
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
</Header>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="Customer">
|
||||
<MudLink Href="@Navigation.Management.Customers.DetailsHref(@context?.Customers?.Id)" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Customers?.Name
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Host">
|
||||
<MudLink Href="@Navigation.Management.Hosts.DetailsHref(@context?.Hosts?.Id)" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Hosts?.Name
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Interface">
|
||||
<MudLink Href="@Navigation.Management.Hosts.Network.Interfaces.DetailsHref(@context?.Hosts?.Id, @context?.Interface?.Id.ToString())" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Interface?.Name
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</TableContainer>
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
using Insight.Infrastructure;
|
||||
using Insight.Infrastructure.Entities;
|
||||
using Insight.Web.Components.Containers;
|
||||
using Insight.Web.Constants;
|
||||
using Insight.Web.Extensions;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using MongoDB.Driver;
|
||||
using MongoDB.Driver.Linq;
|
||||
using MudBlazor;
|
||||
using System.Text.RegularExpressions;
|
||||
using SortDirection = MudBlazor.SortDirection;
|
||||
|
||||
namespace Insight.Web.Pages.Inventory.Network.Nameservers;
|
||||
|
||||
[Route(Navigation.Inventory.Network.Nameservers.Hosts)]
|
||||
public partial class Hosts
|
||||
{
|
||||
[Parameter] public string? NameserverAddress { get; set; }
|
||||
|
||||
[Inject] private IMongoDatabase Database { get; init; } = default!;
|
||||
[Inject] private ISnackbar Snackbar { get; init; } = default!;
|
||||
[Inject] private NavigationManager NavigationManager { get; init; } = default!;
|
||||
|
||||
private TableContainer<ViewModel>? Container { get; set; }
|
||||
private string Title { get; set; } = Global.Name;
|
||||
private List<BreadcrumbItem> Breadcrumbs { get; } = new();
|
||||
private string? Search { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
NameserverAddress = NameserverAddress?.UriEscape(true);
|
||||
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Home", href: Navigation.Home));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Inventory", href: "#", true));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Nameservers", href: Navigation.Inventory.Network.Nameservers.Index));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(NameserverAddress))
|
||||
{
|
||||
Notification.Error(Snackbar, "Not Found");
|
||||
NavigationManager.NavigateTo(Navigation.Inventory.Network.Nameservers.Index);
|
||||
return;
|
||||
}
|
||||
|
||||
Title = $"Inventory » Nameserver » {NameserverAddress} » Hosts|Insight";
|
||||
Breadcrumbs.Add(new BreadcrumbItem(NameserverAddress, href: "#", true));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Hosts", href: "#", true));
|
||||
}
|
||||
|
||||
private async Task<TableData<ViewModel>> LoadDataAsync(TableState state)
|
||||
{
|
||||
try
|
||||
{
|
||||
var filter = Builders<BsonDocument>.Filter.Empty;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Search) is false)
|
||||
{
|
||||
var regex = new BsonRegularExpression(new Regex(Search, RegexOptions.IgnoreCase));
|
||||
filter &= Builders<BsonDocument>.Filter.Regex("customer.name", regex) |
|
||||
Builders<BsonDocument>.Filter.Regex("host.name", regex) |
|
||||
Builders<BsonDocument>.Filter.Regex("interface", regex);
|
||||
}
|
||||
|
||||
var query = Database.HostInterfaceNameserver()
|
||||
.Aggregate()
|
||||
.Match(Builders<HostInterfaceNameserverEntity>.Filter.Eq(p => p.Address, NameserverAddress))
|
||||
.Lookup("host", "_host", "_id", "hosts")
|
||||
.Match(new BsonDocument("hosts", new BsonDocument
|
||||
{
|
||||
{ "$exists", true },
|
||||
{ "$ne", new BsonArray() }
|
||||
}))
|
||||
.Lookup("customer", "hosts._customer", "_id", "customers")
|
||||
.Lookup("host_if", "_interface", "_id", "interfaces")
|
||||
.Project(new BsonDocument
|
||||
{
|
||||
{ "_id", 0 },
|
||||
{ "interface", new BsonDocument("$first", "$interfaces") },
|
||||
{ "host", new BsonDocument("$first", "$hosts") },
|
||||
{ "customer", new BsonDocument("$first", "$customers") },
|
||||
})
|
||||
.Match(filter)
|
||||
.Sort(state.SortDirection switch
|
||||
{
|
||||
SortDirection.Ascending => state.SortLabel switch
|
||||
{
|
||||
"Customer" => Builders<BsonDocument>.Sort.Ascending("customer.name"),
|
||||
"Host" => Builders<BsonDocument>.Sort.Ascending("host.name"),
|
||||
"Interface" => Builders<BsonDocument>.Sort.Ascending("interface.name"),
|
||||
_ => null
|
||||
},
|
||||
SortDirection.Descending => state.SortLabel switch
|
||||
{
|
||||
"Customer" => Builders<BsonDocument>.Sort.Descending("customer.name"),
|
||||
"Host" => Builders<BsonDocument>.Sort.Descending("host.name"),
|
||||
"Interface" => Builders<BsonDocument>.Sort.Descending("interface.name"),
|
||||
_ => null
|
||||
},
|
||||
_ => Builders<BsonDocument>.Sort.Ascending("customer.name")
|
||||
});
|
||||
|
||||
var countResult = await query.Count().FirstOrDefaultAsync(default);
|
||||
var itemResult = await query.Skip(state.Page * state.PageSize).Limit(state.PageSize).ToListAsync(default);
|
||||
|
||||
return new TableData<ViewModel>()
|
||||
{
|
||||
TotalItems = countResult is null ? 0 : (int)countResult.Count,
|
||||
Items = itemResult.Select(x => BsonSerializer.Deserialize<ViewModel>(x))
|
||||
};
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Notification.Error(Snackbar);
|
||||
return new TableData<ViewModel>();
|
||||
}
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
public class ViewModel
|
||||
{
|
||||
[BsonElement("interface")]
|
||||
public HostInterfaceEntity? Interface { get; set; }
|
||||
|
||||
[BsonElement("host")]
|
||||
public HostEntity? Hosts { get; set; }
|
||||
|
||||
[BsonElement("customer")]
|
||||
public CustomerEntity? Customers { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
@inherits ComponentBase
|
||||
@using Insight.Web.Extensions
|
||||
|
||||
<TableContainer T="ViewModel"
|
||||
@ref="Container"
|
||||
@bind-Search="Search"
|
||||
Title="@Title"
|
||||
Breadcrumbs="@Breadcrumbs"
|
||||
Data="LoadDataAsync">
|
||||
<Header>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Address" T="ViewModel">
|
||||
Address
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Hosts" T="ViewModel">
|
||||
Hosts
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
</Header>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="Address">
|
||||
@context?.Address
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Hosts">
|
||||
<MudLink Href="@Navigation.Inventory.Network.Nameservers.HostsHref(context?.Address?.UriEscape())" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Hosts
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</TableContainer>
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
using Insight.Infrastructure;
|
||||
using Insight.Infrastructure.Entities;
|
||||
using Insight.Web.Components.Containers;
|
||||
using Insight.Web.Constants;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using MongoDB.Driver;
|
||||
using MudBlazor;
|
||||
using System.Text.RegularExpressions;
|
||||
using SortDirection = MudBlazor.SortDirection;
|
||||
|
||||
namespace Insight.Web.Pages.Inventory.Network.Nameservers;
|
||||
|
||||
[Route(Navigation.Inventory.Network.Nameservers.Index)]
|
||||
public partial class Index
|
||||
{
|
||||
[Inject] private IMongoDatabase Database { get; init; } = default!;
|
||||
[Inject] private ISnackbar Snackbar { get; init; } = default!;
|
||||
|
||||
private TableContainer<ViewModel>? Container { get; set; }
|
||||
private string Title { get; set; } = Global.Name;
|
||||
private List<BreadcrumbItem> Breadcrumbs { get; } = new();
|
||||
private string? Search { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Title = $"Inventory » Nameservers|Insight";
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Home", href: Navigation.Home));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Inventory", href: "#", true));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Nameservers", href: "#", true));
|
||||
}
|
||||
|
||||
private async Task<TableData<ViewModel>> LoadDataAsync(TableState state)
|
||||
{
|
||||
try
|
||||
{
|
||||
var filter = Builders<HostInterfaceNameserverEntity>.Filter.Empty;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Search) is false)
|
||||
{
|
||||
var regex = new BsonRegularExpression(new Regex(Search, RegexOptions.IgnoreCase));
|
||||
filter &= Builders<HostInterfaceNameserverEntity>.Filter.Regex(x => x.Address, regex);
|
||||
}
|
||||
|
||||
var query = Database.HostInterfaceNameserver()
|
||||
.Aggregate()
|
||||
.Match(filter)
|
||||
.Lookup("host", "_host", "_id", "hosts")
|
||||
.Match(new BsonDocument("hosts", new BsonDocument
|
||||
{
|
||||
{ "$exists", true },
|
||||
{ "$ne", new BsonArray() }
|
||||
}))
|
||||
.Group(new BsonDocument
|
||||
{
|
||||
{ "_id", new BsonDocument("address", "$address") },
|
||||
{ "address", new BsonDocument("$first", "$address") },
|
||||
{ "hosts", new BsonDocument("$addToSet", "$hosts") },
|
||||
})
|
||||
.Project(new BsonDocument
|
||||
{
|
||||
{ "_id", 0 },
|
||||
{ "address", 1 },
|
||||
{ "hosts", 1 },
|
||||
{ "hosts_size", new BsonDocument("$size", "$hosts") },
|
||||
})
|
||||
.Sort(state.SortDirection switch
|
||||
{
|
||||
SortDirection.Ascending => state.SortLabel switch
|
||||
{
|
||||
"Address" => Builders<BsonDocument>.Sort.Ascending("address"),
|
||||
"Hosts" => Builders<BsonDocument>.Sort.Ascending("hosts_size"),
|
||||
_ => null
|
||||
},
|
||||
SortDirection.Descending => state.SortLabel switch
|
||||
{
|
||||
"Address" => Builders<BsonDocument>.Sort.Descending("address"),
|
||||
"Hosts" => Builders<BsonDocument>.Sort.Descending("hosts_size"),
|
||||
_ => null
|
||||
},
|
||||
_ => Builders<BsonDocument>.Sort.Descending("hosts_size")
|
||||
});
|
||||
|
||||
var countResult = await query.Count().FirstOrDefaultAsync(default);
|
||||
var itemResult = await query.Skip(state.Page * state.PageSize).Limit(state.PageSize).ToListAsync(default);
|
||||
|
||||
return new TableData<ViewModel>()
|
||||
{
|
||||
TotalItems = countResult is null ? 0 : (int)countResult.Count,
|
||||
Items = itemResult.Select(x => BsonSerializer.Deserialize<ViewModel>(x))
|
||||
};
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Notification.Error(Snackbar);
|
||||
return new TableData<ViewModel>();
|
||||
}
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
public class ViewModel
|
||||
{
|
||||
[BsonElement("address")]
|
||||
public string? Address { get; set; }
|
||||
|
||||
[BsonElement("hosts_size")]
|
||||
public int? Hosts { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
@inherits ComponentBase
|
||||
|
||||
<TableContainer T="ViewModel"
|
||||
@ref="Container"
|
||||
@bind-Search="Search"
|
||||
Title="@Title"
|
||||
Breadcrumbs="@Breadcrumbs"
|
||||
Data="LoadDataAsync">
|
||||
<Header>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Customer" T="ViewModel">
|
||||
Customer
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Host" T="ViewModel">
|
||||
Host
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Interface" T="ViewModel">
|
||||
Interface
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Mask" T="ViewModel">
|
||||
Mask
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Gateway" T="ViewModel">
|
||||
Gateway
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
<MudTh>
|
||||
<MudTableSortLabel SortLabel="Metric" T="ViewModel">
|
||||
Metric
|
||||
</MudTableSortLabel>
|
||||
</MudTh>
|
||||
</Header>
|
||||
<RowTemplate>
|
||||
<MudTd DataLabel="Customer">
|
||||
<MudLink Href="@Navigation.Management.Customers.DetailsHref(@context?.Customers?.Id)" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Customers?.Name
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Host">
|
||||
<MudLink Href="@Navigation.Management.Hosts.DetailsHref(@context?.Hosts?.Id)" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Hosts?.Name
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Interface">
|
||||
<MudLink Href="@Navigation.Management.Hosts.Network.Interfaces.DetailsHref(@context?.Hosts?.Id, @context?.Interface?.Id.ToString())" Typo="Typo.inherit" Underline="Underline.None">
|
||||
@context?.Interface?.Name
|
||||
</MudLink>
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Mask">
|
||||
@context?.Mask
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Gateway">
|
||||
@context?.Gateway
|
||||
</MudTd>
|
||||
<MudTd DataLabel="Metric">
|
||||
@context?.Metric
|
||||
</MudTd>
|
||||
</RowTemplate>
|
||||
</TableContainer>
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
using Insight.Infrastructure;
|
||||
using Insight.Infrastructure.Entities;
|
||||
using Insight.Web.Components.Containers;
|
||||
using Insight.Web.Constants;
|
||||
using Insight.Web.Extensions;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using MongoDB.Driver;
|
||||
using MongoDB.Driver.Linq;
|
||||
using MudBlazor;
|
||||
using System.Text.RegularExpressions;
|
||||
using SortDirection = MudBlazor.SortDirection;
|
||||
|
||||
namespace Insight.Web.Pages.Inventory.Network.Routes;
|
||||
|
||||
[Route(Navigation.Inventory.Network.Routes.Hosts)]
|
||||
public partial class Hosts
|
||||
{
|
||||
[Parameter] public string? RouteAddress { get; set; }
|
||||
|
||||
[Inject] private IMongoDatabase Database { get; init; } = default!;
|
||||
[Inject] private ISnackbar Snackbar { get; init; } = default!;
|
||||
[Inject] private NavigationManager NavigationManager { get; init; } = default!;
|
||||
|
||||
private TableContainer<ViewModel>? Container { get; set; }
|
||||
private string Title { get; set; } = Global.Name;
|
||||
private List<BreadcrumbItem> Breadcrumbs { get; } = new();
|
||||
private string? Search { get; set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
RouteAddress = RouteAddress?.UriEscape(true);
|
||||
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Home", href: Navigation.Home));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Inventory", href: "#", true));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Routes", href: Navigation.Inventory.Network.Routes.Index));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(RouteAddress))
|
||||
{
|
||||
Notification.Error(Snackbar, "Not Found");
|
||||
NavigationManager.NavigateTo(Navigation.Inventory.Network.Routes.Index);
|
||||
return;
|
||||
}
|
||||
|
||||
Title = $"Inventory » Route » {RouteAddress} » Hosts|Insight";
|
||||
Breadcrumbs.Add(new BreadcrumbItem(RouteAddress, href: "#", true));
|
||||
Breadcrumbs.Add(new BreadcrumbItem("Hosts", href: "#", true));
|
||||
}
|
||||
|
||||
private async Task<TableData<ViewModel>> LoadDataAsync(TableState state)
|
||||
{
|
||||
try
|
||||
{
|
||||
var filter = Builders<BsonDocument>.Filter.Empty;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Search) is false)
|
||||
{
|
||||
var regex = new BsonRegularExpression(new Regex(Search, RegexOptions.IgnoreCase));
|
||||
filter &= Builders<BsonDocument>.Filter.Regex("customer.name", regex) |
|
||||
Builders<BsonDocument>.Filter.Regex("host.name", regex) |
|
||||
Builders<BsonDocument>.Filter.Regex("interface", regex);
|
||||
}
|
||||
|
||||
var query = Database.HostInterfaceRoute()
|
||||
.Aggregate()
|
||||
.Match(Builders<HostInterfaceRouteEntity>.Filter.Eq(p => p.Destination, RouteAddress))
|
||||
.Lookup("host", "_host", "_id", "hosts")
|
||||
.Match(new BsonDocument("hosts", new BsonDocument
|
||||
{
|
||||
{ "$exists", true },
|
||||
{ "$ne", new BsonArray() }
|
||||
}))
|
||||
.Lookup("customer", "hosts._customer", "_id", "customers")
|
||||
.Lookup("host_if", "_interface", "_id", "interfaces")
|
||||
.Project(new BsonDocument
|
||||
{
|
||||
{ "_id", 0 },
|
||||
{ "gateway", 1 },
|
||||
{ "mask", 1 },
|
||||
{ "metric", 1 },
|
||||
{ "interface", new BsonDocument("$first", "$interfaces") },
|
||||
{ "host", new BsonDocument("$first", "$hosts") },
|
||||
{ "customer", new BsonDocument("$first", "$customers") },
|
||||
})
|
||||
.Match(filter)
|
||||
.Sort(state.SortDirection switch
|
||||
{
|
||||
SortDirection.Ascending => state.SortLabel switch
|
||||
{
|
||||
"Customer" => Builders<BsonDocument>.Sort.Ascending("customer.name"),
|
||||
"Host" => Builders<BsonDocument>.Sort.Ascending("host.name"),
|
||||
"Interface" => Builders<BsonDocument>.Sort.Ascending("interface.name"),
|
||||
_ => null
|
||||
},
|
||||
SortDirection.Descending => state.SortLabel switch
|
||||
{
|
||||
"Customer" => Builders<BsonDocument>.Sort.Descending("customer.name"),
|
||||
"Host" => Builders<BsonDocument>.Sort.Descending("host.name"),
|
||||
"Interface" => Builders<BsonDocument>.Sort.Descending("interface.name"),
|
||||
_ => null
|
||||
},
|
||||
_ => Builders<BsonDocument>.Sort.Ascending("customer.name")
|
||||
});
|
||||
|
||||
var countResult = await query.Count().FirstOrDefaultAsync(default);
|
||||
var itemResult = await query.Skip(state.Page * state.PageSize).Limit(state.PageSize).ToListAsync(default);
|
||||
|
||||
return new TableData<ViewModel>()
|
||||
{
|
||||
TotalItems = countResult is null ? 0 : (int)countResult.Count,
|
||||
Items = itemResult.Select(x => BsonSerializer.Deserialize<ViewModel>(x))
|
||||
};
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Notification.Error(Snackbar);
|
||||
return new TableData<ViewModel>();
|
||||
}
|
||||
}
|
||||
|
||||
[BsonIgnoreExtraElements]
|
||||
public class ViewModel
|
||||
{
|
||||
[BsonElement("gateway")]
|
||||
public string? Gateway { get; set; }
|
||||
|
||||
[BsonElement("mask")]
|
||||
public string? Mask { get; set; }
|
||||
|
||||
[BsonElement("metric")]
|
||||
public int? Metric { get; set; }
|
||||
|
||||
[BsonElement("interface")]
|
||||
public HostInterfaceEntity? Interface { get; set; }
|
||||
|
||||
[BsonElement("host")]
|
||||
public HostEntity? Hosts { get; set; }
|
||||
|
||||
[BsonElement("customer")]
|
||||
public CustomerEntity? Customers { get; set; }
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue