mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-05-18 05:18:05 -04:00
Merge pull request #193 from lanedirt/80-add-vault-auth-attempt-logging-bugfix
Update admin logs path as /logs folder doesn't work correctly due to …
This commit is contained in:
@@ -48,9 +48,4 @@
|
||||
<ProjectReference Include="..\Utilities\AliasVault.RazorComponents\AliasVault.RazorComponents.csproj" />
|
||||
<ProjectReference Include="..\Utilities\Cryptography\Cryptography.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<_ContentIncludedByDefault Remove="Main\Components\Refresh\RefreshButton.razor" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
149
src/AliasVault.Admin/Main/Pages/Logging/Auth.razor
Normal file
149
src/AliasVault.Admin/Main/Pages/Logging/Auth.razor
Normal file
@@ -0,0 +1,149 @@
|
||||
@page "/logging/auth"
|
||||
@using AliasVault.Shared.Models.Enums
|
||||
@inherits MainBase
|
||||
|
||||
<LayoutPageTitle>Auth logs</LayoutPageTitle>
|
||||
|
||||
<div class="grid grid-cols-1 px-4 pt-6 xl:grid-cols-3 xl:gap-4 dark:bg-gray-900">
|
||||
<div class="mb-4 col-span-full xl:mb-2">
|
||||
<Breadcrumb BreadcrumbItems="BreadcrumbItems" />
|
||||
<div class="flex items-center justify-between">
|
||||
<h1 class="text-xl font-semibold text-gray-900 sm:text-2xl dark:text-white">Auth logs</h1>
|
||||
<RefreshButton OnRefresh="RefreshData" ButtonText="Refresh" />
|
||||
</div>
|
||||
<p>This page gives an overview of recent auth attempts.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (IsLoading)
|
||||
{
|
||||
<LoadingIndicator />
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="px-4">
|
||||
<Paginator CurrentPage="CurrentPage" PageSize="PageSize" TotalRecords="TotalRecords" OnPageChanged="HandlePageChanged" />
|
||||
|
||||
<div class="mb-4 flex space-x-4">
|
||||
<div class="flex w-full">
|
||||
<div class="w-2/3 pr-2">
|
||||
<input type="text" @bind-value="SearchTerm" @bind-value:event="oninput" id="search" placeholder="Search logs..." class="w-full px-4 py-2 border rounded text-sm text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||
</div>
|
||||
<div class="w-1/3 pl-2">
|
||||
<select @bind="SelectedEventType" class="w-full px-4 py-2 border rounded text-sm text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||
<option value="">All event types</option>
|
||||
@foreach (var eventType in Enum.GetValues<AuthEventType>())
|
||||
{
|
||||
<option value="@eventType">@eventType</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<table class="w-full text-sm text-left text-gray-500 shadow rounded border">
|
||||
<thead class="text-xs text-gray-700 uppercase bg-gray-50">
|
||||
<tr>
|
||||
<th scope="col" class="px-4 py-3">ID</th>
|
||||
<th scope="col" class="px-4 py-3">Time</th>
|
||||
<th scope="col" class="px-4 py-3">Username</th>
|
||||
<th scope="col" class="px-4 py-3">Event</th>
|
||||
<th scope="col" class="px-4 py-3">Success</th>
|
||||
<th scope="col" class="px-4 py-3">IP</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var log in LogList)
|
||||
{
|
||||
<tr class="bg-white border-b hover:bg-gray-50">
|
||||
<td class="px-4 py-3 font-medium text-gray-900">@log.Id</td>
|
||||
<td class="px-4 py-3">@log.Timestamp.ToString("yyyy-MM-dd HH:mm")</td>
|
||||
<td class="px-4 py-3">@log.Username</td>
|
||||
<td class="px-4 py-3">@log.EventType</td>
|
||||
<td class="px-4 py-3"><StatusPill Enabled="log.IsSuccess" TextTrue="Success" TextFalse="Failed" /></td>
|
||||
<td class="px-4 py-3">@log.IpAddress</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
private List<AuthLog> LogList { get; set; } = [];
|
||||
private bool IsLoading { get; set; } = true;
|
||||
private int CurrentPage { get; set; } = 1;
|
||||
private int PageSize { get; set; } = 50;
|
||||
private int TotalRecords { get; set; }
|
||||
|
||||
private string _searchTerm = string.Empty;
|
||||
private string SearchTerm
|
||||
{
|
||||
get => _searchTerm;
|
||||
set
|
||||
{
|
||||
if (_searchTerm != value)
|
||||
{
|
||||
_searchTerm = value;
|
||||
_ = RefreshData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _selectedEventType = string.Empty;
|
||||
private string SelectedEventType
|
||||
{
|
||||
get => _selectedEventType;
|
||||
set
|
||||
{
|
||||
if (_selectedEventType != value)
|
||||
{
|
||||
_selectedEventType = value;
|
||||
_ = RefreshData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await RefreshData();
|
||||
}
|
||||
|
||||
private void HandlePageChanged(int newPage)
|
||||
{
|
||||
CurrentPage = newPage;
|
||||
_ = RefreshData();
|
||||
}
|
||||
|
||||
private async Task RefreshData()
|
||||
{
|
||||
IsLoading = true;
|
||||
StateHasChanged();
|
||||
|
||||
var query = DbContext.AuthLogs.AsQueryable();
|
||||
|
||||
if (!string.IsNullOrEmpty(SearchTerm))
|
||||
{
|
||||
query = query.Where(x => EF.Functions.Like(x.Username.ToLower(), "%" + SearchTerm.ToLower() + "%"));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(SelectedEventType))
|
||||
{
|
||||
var success = Enum.TryParse<AuthEventType>(SelectedEventType, out var eventType);
|
||||
if (success)
|
||||
{
|
||||
query = query.Where(x => x.EventType == eventType);
|
||||
}
|
||||
}
|
||||
|
||||
TotalRecords = await query.CountAsync();
|
||||
LogList = await query
|
||||
.OrderByDescending(x => x.Timestamp)
|
||||
.Skip((CurrentPage - 1) * PageSize)
|
||||
.Take(PageSize)
|
||||
.ToListAsync();
|
||||
|
||||
IsLoading = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
168
src/AliasVault.Admin/Main/Pages/Logging/General.razor
Normal file
168
src/AliasVault.Admin/Main/Pages/Logging/General.razor
Normal file
@@ -0,0 +1,168 @@
|
||||
@page "/logging/general"
|
||||
@inherits MainBase
|
||||
|
||||
<LayoutPageTitle>System logs</LayoutPageTitle>
|
||||
|
||||
<div class="grid grid-cols-1 px-4 pt-6 xl:grid-cols-3 xl:gap-4 dark:bg-gray-900">
|
||||
<div class="mb-4 col-span-full xl:mb-2">
|
||||
<Breadcrumb BreadcrumbItems="BreadcrumbItems" />
|
||||
<div class="flex items-center justify-between">
|
||||
<h1 class="text-xl font-semibold text-gray-900 sm:text-2xl dark:text-white">General logs</h1>
|
||||
<RefreshButton OnRefresh="RefreshData" ButtonText="Refresh" />
|
||||
</div>
|
||||
<p>This page gives an overview of recent system logs.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (IsLoading)
|
||||
{
|
||||
<LoadingIndicator />
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="px-4">
|
||||
<Paginator CurrentPage="CurrentPage" PageSize="PageSize" TotalRecords="TotalRecords" OnPageChanged="HandlePageChanged" />
|
||||
|
||||
<div class="mb-4 flex space-x-4">
|
||||
<div class="flex w-full">
|
||||
<div class="w-2/3 pr-2">
|
||||
<input type="text" @bind-value="SearchTerm" @bind-value:event="oninput" id="search" placeholder="Search logs..." class="w-full px-4 py-2 border rounded text-sm text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||
</div>
|
||||
<div class="w-1/3 pl-2">
|
||||
<select @bind="SelectedServiceName" class="w-full px-4 py-2 border rounded text-sm text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||
<option value="">All Services</option>
|
||||
@foreach (var service in ServiceNames)
|
||||
{
|
||||
<option value="@service">@service</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<table class="w-full text-sm text-left text-gray-500 shadow rounded border">
|
||||
<thead class="text-xs text-gray-700 uppercase bg-gray-50">
|
||||
<tr>
|
||||
<th scope="col" class="px-4 py-3">ID</th>
|
||||
<th scope="col" class="px-4 py-3">Time</th>
|
||||
<th scope="col" class="px-4 py-3">Application</th>
|
||||
<th scope="col" class="px-4 py-3">Level</th>
|
||||
<th scope="col" class="px-4 py-3">Message</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var log in LogList)
|
||||
{
|
||||
<tr class="bg-white border-b hover:bg-gray-50">
|
||||
<td class="px-4 py-3 font-medium text-gray-900">@log.Id</td>
|
||||
<td class="px-4 py-3">@log.TimeStamp.ToString("yyyy-MM-dd HH:mm")</td>
|
||||
<td class="px-4 py-3">@log.Application</td>
|
||||
|
||||
@{
|
||||
string bgColor = log.Level switch
|
||||
{
|
||||
"Information" => "bg-blue-500",
|
||||
"Error" => "bg-red-500",
|
||||
"Warning" => "bg-yellow-500",
|
||||
"Debug" => "bg-green-500",
|
||||
_ => "bg-gray-500"
|
||||
};
|
||||
}
|
||||
<td class="px-4 py-3">
|
||||
<span class="px-2 py-1 rounded-full text-white @bgColor">
|
||||
@log.Level
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-4 py-3" title="@log.Exception">
|
||||
@if (log.SourceContext.Length > 0)
|
||||
{
|
||||
<span>@log.SourceContext: </span>
|
||||
}
|
||||
@log.Message
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
private List<Log> LogList { get; set; } = [];
|
||||
private bool IsLoading { get; set; } = true;
|
||||
private int CurrentPage { get; set; } = 1;
|
||||
private int PageSize { get; set; } = 50;
|
||||
private int TotalRecords { get; set; }
|
||||
|
||||
private string _searchTerm = string.Empty;
|
||||
private string SearchTerm
|
||||
{
|
||||
get => _searchTerm;
|
||||
set
|
||||
{
|
||||
if (_searchTerm != value)
|
||||
{
|
||||
_searchTerm = value;
|
||||
_ = RefreshData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _selectedServiceName = string.Empty;
|
||||
private string SelectedServiceName
|
||||
{
|
||||
get => _selectedServiceName;
|
||||
set
|
||||
{
|
||||
if (_selectedServiceName != value)
|
||||
{
|
||||
_selectedServiceName = value;
|
||||
_ = RefreshData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<string> ServiceNames { get; set; } = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
ServiceNames = await DbContext.Logs.Select(l => l.Application).Distinct().ToListAsync();
|
||||
await RefreshData();
|
||||
}
|
||||
|
||||
private void HandlePageChanged(int newPage)
|
||||
{
|
||||
CurrentPage = newPage;
|
||||
_ = RefreshData();
|
||||
}
|
||||
|
||||
private async Task RefreshData()
|
||||
{
|
||||
IsLoading = true;
|
||||
StateHasChanged();
|
||||
|
||||
var query = DbContext.Logs.AsQueryable();
|
||||
|
||||
if (!string.IsNullOrEmpty(SearchTerm))
|
||||
{
|
||||
query = query.Where(x => EF.Functions.Like(x.Application.ToLower(), "%" + SearchTerm.ToLower() + "%") ||
|
||||
EF.Functions.Like(x.Message.ToLower(), "%" + SearchTerm.ToLower() + "%") ||
|
||||
EF.Functions.Like(x.Level.ToLower(), "%" + SearchTerm.ToLower() + "%"));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(SelectedServiceName))
|
||||
{
|
||||
query = query.Where(x => x.Application == SelectedServiceName);
|
||||
}
|
||||
|
||||
TotalRecords = await query.CountAsync();
|
||||
LogList = await query
|
||||
.OrderByDescending(x => x.Id)
|
||||
.Skip((CurrentPage - 1) * PageSize)
|
||||
.Take(PageSize)
|
||||
.ToListAsync();
|
||||
|
||||
IsLoading = false;
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user