diff --git a/src/AliasVault.Admin/Main/Components/Layout/Breadcrumb.razor b/src/AliasVault.Admin/Main/Components/Layout/Breadcrumb.razor deleted file mode 100644 index 8221c3020..000000000 --- a/src/AliasVault.Admin/Main/Components/Layout/Breadcrumb.razor +++ /dev/null @@ -1,52 +0,0 @@ -@inherits ComponentBase - - - -@code { - /// - /// Gets or sets the list of breadcrumb items. - /// - [Parameter] - public List BreadcrumbItems { get; set; } = new(); - - /// - protected override void OnInitialized() - { - base.OnInitialized(); - // Remove first item if it is the home page - if (BreadcrumbItems.Any() && BreadcrumbItems[0].DisplayName == "Home") - { - BreadcrumbItems.RemoveAt(0); - } - } -} diff --git a/src/AliasVault.Admin/Main/Pages/Account/Manage/ChangePassword.razor b/src/AliasVault.Admin/Main/Pages/Account/Manage/ChangePassword.razor index 746f1a4a3..3a015fe7e 100644 --- a/src/AliasVault.Admin/Main/Pages/Account/Manage/ChangePassword.razor +++ b/src/AliasVault.Admin/Main/Pages/Account/Manage/ChangePassword.razor @@ -29,9 +29,7 @@
- + Update password
diff --git a/src/AliasVault.Admin/Main/Pages/Account/Manage/EnableAuthenticator.razor b/src/AliasVault.Admin/Main/Pages/Account/Manage/EnableAuthenticator.razor index bf41931bd..e2f3c539b 100644 --- a/src/AliasVault.Admin/Main/Pages/Account/Manage/EnableAuthenticator.razor +++ b/src/AliasVault.Admin/Main/Pages/Account/Manage/EnableAuthenticator.razor @@ -52,9 +52,7 @@ else
- + Verify
diff --git a/src/AliasVault.Admin/Main/Pages/Account/Manage/Index.razor b/src/AliasVault.Admin/Main/Pages/Account/Manage/Index.razor index fe21aa3cc..f6340b935 100644 --- a/src/AliasVault.Admin/Main/Pages/Account/Manage/Index.razor +++ b/src/AliasVault.Admin/Main/Pages/Account/Manage/Index.razor @@ -22,9 +22,7 @@
- + Save
diff --git a/src/AliasVault.Admin/Main/Pages/Account/Manage/ResetAuthenticator.razor b/src/AliasVault.Admin/Main/Pages/Account/Manage/ResetAuthenticator.razor index 1056f3d6a..cab5eb930 100644 --- a/src/AliasVault.Admin/Main/Pages/Account/Manage/ResetAuthenticator.razor +++ b/src/AliasVault.Admin/Main/Pages/Account/Manage/ResetAuthenticator.razor @@ -23,7 +23,7 @@
- + Reset authenticator key
diff --git a/src/AliasVault.Admin/Main/Pages/Account/Manage/TwoFactorAuthentication.razor b/src/AliasVault.Admin/Main/Pages/Account/Manage/TwoFactorAuthentication.razor index b10fc8b1b..1bff518c1 100644 --- a/src/AliasVault.Admin/Main/Pages/Account/Manage/TwoFactorAuthentication.razor +++ b/src/AliasVault.Admin/Main/Pages/Account/Manage/TwoFactorAuthentication.razor @@ -46,18 +46,12 @@ diff --git a/src/AliasVault.Admin/Main/Pages/Account/ManageLayout.razor b/src/AliasVault.Admin/Main/Pages/Account/ManageLayout.razor index f4e2ae308..0a5f761ae 100644 --- a/src/AliasVault.Admin/Main/Pages/Account/ManageLayout.razor +++ b/src/AliasVault.Admin/Main/Pages/Account/ManageLayout.razor @@ -2,14 +2,11 @@ @using AliasVault.Admin.Main.Layout @layout MainLayout -
-
-
-

Manage account

-
-

Manage your profile here.

-
-
+ +

@@ -22,3 +19,19 @@
+ +@code { + /// + /// Gets the breadcrumb items for the page. A default set of breadcrumbs is added in the parent OnInitialized method. + /// + private List BreadcrumbItems { get; } = new(); + + /// + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync(); + + // Add base breadcrumbs. + BreadcrumbItems.Add(new BreadcrumbItem { DisplayName = "Home", Url = "/" }); + } +} diff --git a/src/AliasVault.Admin/Main/Pages/Emails.razor b/src/AliasVault.Admin/Main/Pages/Emails.razor index be6c33298..49a64cde7 100644 --- a/src/AliasVault.Admin/Main/Pages/Emails.razor +++ b/src/AliasVault.Admin/Main/Pages/Emails.razor @@ -1,18 +1,17 @@ @page "/emails" +@using AliasVault.RazorComponents.Tables @inherits MainBase Emails -
-
- -
-

Emails

- -
-

This page gives an overview of recently received mails by this AliasVault server.

-
-
+ + + + + @if (IsLoading) { @@ -23,59 +22,66 @@ else
- - - - - - - - - - + + @foreach (var email in EmailList) + { + + + + + + + + - - - @foreach (var email in EmailList) - { - - - - - - - - - - } - -
IDTimeFromToSubjectPreviewAttachments
+ @email.Id + + @email.DateSystem.ToString("yyyy-MM-dd HH:mm") + + @(email.FromLocal.Length > 15 ? email.FromLocal.Substring(0, 15) : email.FromLocal)@@@(email.FromDomain.Length > 15 ? email.FromDomain.Substring(0, 15) : email.FromDomain) + + @email.ToLocal@@@email.ToDomain + + @(email.Subject.Length > 30 ? email.Subject.Substring(0, 30) : email.Subject) + + + @(email.MessagePreview?.Length > 30 ? email.MessagePreview.Substring(0, 30) : email.MessagePreview) + + + @email.Attachments.Count +
- @email.Id - - @email.DateSystem.ToString("yyyy-MM-dd HH:mm") - - @(email.FromLocal.Length > 15 ? email.FromLocal.Substring(0, 15) : email.FromLocal)@@@(email.FromDomain.Length > 15 ? email.FromDomain.Substring(0, 15) : email.FromDomain) - - @email.ToLocal@@@email.ToDomain - - @(email.Subject.Length > 30 ? email.Subject.Substring(0, 30) : email.Subject) - - - @(email.MessagePreview?.Length > 30 ? email.MessagePreview.Substring(0, 30) : email.MessagePreview) - - - @email.Attachments.Count -
+ } +
} @code { + private readonly List _tableColumns = [ + new TableColumn { Title = "ID", PropertyName = "Id" }, + new TableColumn { Title = "Time", PropertyName = "DateSystem" }, + new TableColumn { Title = "From", PropertyName = "From" }, + new TableColumn { Title = "To", PropertyName = "To" }, + new TableColumn { Title = "Subject", PropertyName = "Subject" }, + new TableColumn { Title = "Preview", PropertyName = "MessagePreview" }, + new TableColumn { Title = "Attachments", PropertyName = "Attachments" }, + ]; + private List EmailList { 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 SortColumn { get; set; } = "Id"; + private SortDirection SortDirection { get; set; } = SortDirection.Descending; + + private async Task HandleSortChanged((string column, SortDirection direction) sort) + { + SortColumn = sort.column; + SortDirection = sort.direction; + await RefreshData(); + } + /// protected override async Task OnAfterRenderAsync(bool firstRender) { @@ -96,9 +102,53 @@ else IsLoading = true; StateHasChanged(); - TotalRecords = await DbContext.Emails.CountAsync(); - EmailList = await DbContext.Emails - .OrderByDescending(x => x.DateSystem) + IQueryable query = DbContext.Emails; + + // Apply sort + switch (SortColumn) + { + case "Id": + query = SortDirection == SortDirection.Ascending + ? query.OrderBy(x => x.Id) + : query.OrderByDescending(x => x.Id); + break; + case "DateSystem": + query = SortDirection == SortDirection.Ascending + ? query.OrderBy(x => x.DateSystem) + : query.OrderByDescending(x => x.DateSystem); + break; + case "From": + query = SortDirection == SortDirection.Ascending + ? query.OrderBy(x => x.FromLocal + "@" + x.FromDomain) + : query.OrderByDescending(x => x.FromLocal + "@" + x.FromDomain); + break; + case "To": + query = SortDirection == SortDirection.Ascending + ? query.OrderBy(x => x.ToLocal + "@" + x.ToDomain) + : query.OrderByDescending(x => x.ToLocal + "@" + x.ToDomain); + break; + case "Subject": + query = SortDirection == SortDirection.Ascending + ? query.OrderBy(x => x.Subject) + : query.OrderByDescending(x => x.Subject); + break; + case "MessagePreview": + query = SortDirection == SortDirection.Ascending + ? query.OrderBy(x => x.MessagePreview) + : query.OrderByDescending(x => x.MessagePreview); + break; + case "Attachments": + query = SortDirection == SortDirection.Ascending + ? query.OrderBy(x => x.Attachments.Count) + : query.OrderByDescending(x => x.Attachments.Count); + break; + default: + query = query.OrderByDescending(x => x.DateSystem); + break; + } + + TotalRecords = await query.CountAsync(); + EmailList = await query .Skip((CurrentPage - 1) * PageSize) .Take(PageSize) .ToListAsync(); diff --git a/src/AliasVault.Admin/Main/Pages/Home.razor b/src/AliasVault.Admin/Main/Pages/Home.razor index 60deb63cb..b5867c293 100644 --- a/src/AliasVault.Admin/Main/Pages/Home.razor +++ b/src/AliasVault.Admin/Main/Pages/Home.razor @@ -3,13 +3,8 @@ Home -
-
- -
-

AliasVault Admin

-
-

Welcome to the AliasVault admin portal.

-
-
- + + diff --git a/src/AliasVault.Admin/Main/Pages/Logging/Auth.razor b/src/AliasVault.Admin/Main/Pages/Logging/Auth.razor index b44a8d349..7c76d4081 100644 --- a/src/AliasVault.Admin/Main/Pages/Logging/Auth.razor +++ b/src/AliasVault.Admin/Main/Pages/Logging/Auth.razor @@ -1,22 +1,19 @@ @page "/logging/auth" +@using AliasVault.RazorComponents.Tables @using AliasVault.Shared.Models.Enums @inherits MainBase Auth logs -
-
- -
-

Auth logs

-
- - -
-
-

This page gives an overview of recent auth attempts.

-
-
+ + + + + + @if (IsLoading) { @@ -43,6 +40,21 @@ else + + + @foreach (var log in LogList) + { + + @log.Id + @log.Timestamp.ToString("yyyy-MM-dd HH:mm") + @log.Username + @log.EventType + + @log.IpAddress + + } + + @@ -72,6 +84,15 @@ else } @code { + private readonly List _tableColumns = [ + new TableColumn { Title = "ID", PropertyName = "Id" }, + new TableColumn { Title = "Time", PropertyName = "Timestamp" }, + new TableColumn { Title = "Username", PropertyName = "Username" }, + new TableColumn { Title = "Event", PropertyName = "EventType" }, + new TableColumn { Title = "Success", PropertyName = "IsSuccess" }, + new TableColumn { Title = "IP", PropertyName = "IpAddress" }, + ]; + private List LogList { get; set; } = []; private bool IsLoading { get; set; } = true; private int CurrentPage { get; set; } = 1; @@ -106,6 +127,16 @@ else } } + private string SortColumn { get; set; } = "Id"; + private SortDirection SortDirection { get; set; } = SortDirection.Descending; + + private async Task HandleSortChanged((string column, SortDirection direction) sort) + { + SortColumn = sort.column; + SortDirection = sort.direction; + await RefreshData(); + } + /// protected override async Task OnAfterRenderAsync(bool firstRender) { @@ -142,9 +173,10 @@ else } } + query = ApplySort(query); + TotalRecords = await query.CountAsync(); LogList = await query - .OrderByDescending(x => x.Timestamp) .Skip((CurrentPage - 1) * PageSize) .Take(PageSize) .ToListAsync(); @@ -153,6 +185,49 @@ else StateHasChanged(); } + /// + /// Apply sort to the query. + /// + private IQueryable ApplySort(IQueryable query) + { + // Apply sort. + switch (SortColumn) + { + case "Timestamp": + query = SortDirection == SortDirection.Ascending + ? query.OrderBy(x => x.Timestamp) + : query.OrderByDescending(x => x.Timestamp); + break; + case "Username": + query = SortDirection == SortDirection.Ascending + ? query.OrderBy(x => x.Username) + : query.OrderByDescending(x => x.Username); + break; + case "EventType": + query = SortDirection == SortDirection.Ascending + ? query.OrderBy(x => x.EventType) + : query.OrderByDescending(x => x.EventType); + break; + case "IsSuccess": + query = SortDirection == SortDirection.Ascending + ? query.OrderBy(x => x.IsSuccess) + : query.OrderByDescending(x => x.IsSuccess); + break; + case "IpAddress": + query = SortDirection == SortDirection.Ascending + ? query.OrderBy(x => x.IpAddress) + : query.OrderByDescending(x => x.IpAddress); + break; + default: + query = SortDirection == SortDirection.Ascending + ? query.OrderBy(x => x.Id) + : query.OrderByDescending(x => x.Id); + break; + } + + return query; + } + private async Task DeleteLogsWithConfirmation() { if (await ConfirmModalService.ShowConfirmation("Confirm Delete", "Are you sure you want to delete all logs? This action cannot be undone.")) diff --git a/src/AliasVault.Admin/Main/Pages/Logging/General.razor b/src/AliasVault.Admin/Main/Pages/Logging/General.razor index 0c131e3f2..02ab7eb6e 100644 --- a/src/AliasVault.Admin/Main/Pages/Logging/General.razor +++ b/src/AliasVault.Admin/Main/Pages/Logging/General.razor @@ -1,21 +1,18 @@ @page "/logging/general" +@using AliasVault.RazorComponents.Tables @inherits MainBase System logs -
-
- -
-

General logs

-
- - -
-
-

This page gives an overview of recent system logs.

-
-
+ + + + + + @if (IsLoading) { @@ -36,60 +33,57 @@ else @foreach (var service in ServiceNames) { - + } -
- - - - - - - - - - - @foreach (var log in LogList) - { - - - - - @{ - string bgColor = log.Level switch - { - "Information" => "bg-blue-500", - "Error" => "bg-red-500", - "Warning" => "bg-yellow-500", - "Debug" => "bg-green-500", - _ => "bg-gray-500" - }; + + @foreach (var log in LogList) + { + + + + + @{ + string bgColor = log.Level switch + { + "Information" => "bg-blue-500", + "Error" => "bg-red-500", + "Warning" => "bg-yellow-500", + "Debug" => "bg-green-500", + _ => "bg-gray-500" + }; + } + + - - - } - -
IDTimeApplicationLevelMessage
@log.Id@log.TimeStamp.ToString("yyyy-MM-dd HH:mm")@log.Application
@log.Id@log.TimeStamp.ToString("yyyy-MM-dd HH:mm")@log.Application + + @log.Level + + + @if (log.SourceContext.Length > 0) + { + @log.SourceContext: } - - - @log.Level - - - @if (log.SourceContext.Length > 0) - { - @log.SourceContext: - } - @log.Message -
+ @log.Message + + + } + } @code { + private readonly List _tableColumns = [ + new TableColumn { Title = "ID", PropertyName = "Id" }, + new TableColumn { Title = "Time", PropertyName = "Timestamp" }, + new TableColumn { Title = "Application", PropertyName = "Application" }, + new TableColumn { Title = "Level", PropertyName = "Level" }, + new TableColumn { Title = "Message", PropertyName = "Message" }, + ]; + private List LogList { get; set; } = []; private bool IsLoading { get; set; } = true; private int CurrentPage { get; set; } = 1; @@ -126,6 +120,16 @@ else private List ServiceNames { get; set; } = []; + private string SortColumn { get; set; } = "Id"; + private SortDirection SortDirection { get; set; } = SortDirection.Descending; + + private async Task HandleSortChanged((string column, SortDirection direction) sort) + { + SortColumn = sort.column; + SortDirection = sort.direction; + await RefreshData(); + } + /// protected override async Task OnAfterRenderAsync(bool firstRender) { @@ -136,7 +140,6 @@ else } } - private void HandlePageChanged(int newPage) { CurrentPage = newPage; @@ -162,9 +165,38 @@ else query = query.Where(x => x.Application == SelectedServiceName); } + // Apply sort. + switch (SortColumn) + { + case "Application": + query = SortDirection == SortDirection.Ascending + ? query.OrderBy(x => x.Application) + : query.OrderByDescending(x => x.Application); + break; + case "Message": + query = SortDirection == SortDirection.Ascending + ? query.OrderBy(x => x.Message) + : query.OrderByDescending(x => x.Message); + break; + case "Level": + query = SortDirection == SortDirection.Ascending + ? query.OrderBy(x => x.Level) + : query.OrderByDescending(x => x.Level); + break; + case "Timestamp": + query = SortDirection == SortDirection.Ascending + ? query.OrderBy(x => x.TimeStamp) + : query.OrderByDescending(x => x.TimeStamp); + break; + default: + query = SortDirection == SortDirection.Ascending + ? query.OrderBy(x => x.Id) + : query.OrderByDescending(x => x.Id); + break; + } + TotalRecords = await query.CountAsync(); LogList = await query - .OrderByDescending(x => x.Id) .Skip((CurrentPage - 1) * PageSize) .Take(PageSize) .ToListAsync(); diff --git a/src/AliasVault.Admin/Main/Pages/MainBase.cs b/src/AliasVault.Admin/Main/Pages/MainBase.cs index ac144bd0b..9b3ff31f7 100644 --- a/src/AliasVault.Admin/Main/Pages/MainBase.cs +++ b/src/AliasVault.Admin/Main/Pages/MainBase.cs @@ -8,9 +8,9 @@ namespace AliasVault.Admin.Main.Pages; using AliasServerDb; -using AliasVault.Admin.Main.Models; using AliasVault.Admin.Services; using AliasVault.Auth; +using AliasVault.RazorComponents.Models; using AliasVault.RazorComponents.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Components; diff --git a/src/AliasVault.Admin/Main/Pages/Users/Delete.razor b/src/AliasVault.Admin/Main/Pages/Users/Delete.razor index 639964ef2..fdeb53398 100644 --- a/src/AliasVault.Admin/Main/Pages/Users/Delete.razor +++ b/src/AliasVault.Admin/Main/Pages/Users/Delete.razor @@ -3,15 +3,11 @@ Delete user -
-
- -
-

Delete user

-
-

You can delete the user below.

-
-
+ + @if (IsLoading) { @@ -30,13 +26,10 @@ else
@Obj?.UserName
- - - +
+ + +
} diff --git a/src/AliasVault.Admin/Main/Pages/Users/Users.razor b/src/AliasVault.Admin/Main/Pages/Users/Users.razor index c4a134d93..603985ac3 100644 --- a/src/AliasVault.Admin/Main/Pages/Users/Users.razor +++ b/src/AliasVault.Admin/Main/Pages/Users/Users.razor @@ -1,18 +1,17 @@ @page "/users" +@using AliasVault.RazorComponents.Tables @inherits MainBase Users -
-
- -
-

Users

- -
-

This page gives an overview of all registered users and the associated vaults.

-
-
+ + + + + @if (IsLoading) { @@ -26,43 +25,40 @@ else
- - - - - - - - - - - - - - - - @foreach (var user in UserList) - { - - - - - - - - - - - - } - -
IDRegisteredUsername# Vaults# Email claimsStorage2FALast vault updateActions
@user.Id@user.CreatedAt.ToString("yyyy-MM-dd HH:mm")@user.UserName@user.VaultCount@user.EmailClaimCount@Math.Round((double)user.VaultStorageInKb / 1024, 1) MB@user.LastVaultUpdate.ToString("yyyy-MM-dd HH:mm") - View -
+ + + @foreach (var user in UserList) + { + + @user.Id + @user.CreatedAt.ToString("yyyy-MM-dd HH:mm") + @user.UserName + @user.VaultCount + @user.EmailClaimCount + @Math.Round((double)user.VaultStorageInKb / 1024, 1) MB + + @user.LastVaultUpdate.ToString("yyyy-MM-dd HH:mm") + + + + + } + } @code { + private readonly List _tableColumns = [ + new TableColumn { Title = "ID", PropertyName = "Id" }, + new TableColumn { Title = "Registered", PropertyName = "CreatedAt" }, + new TableColumn { Title = "Username", PropertyName = "UserName" }, + new TableColumn { Title = "# Vaults", PropertyName = "VaultCount" }, + new TableColumn { Title = "# Email claims", PropertyName = "EmailClaimCount" }, + new TableColumn { Title = "Storage", PropertyName = "VaultStorageInKb" }, + new TableColumn { Title = "2FA", PropertyName = "TwoFactorEnabled" }, + new TableColumn { Title = "LastVaultUpdate", PropertyName = "LastVaultUpdate" }, + ]; + private List UserList { get; set; } = []; private bool IsLoading { get; set; } = true; private int CurrentPage { get; set; } = 1; @@ -83,6 +79,16 @@ else } } + private string SortColumn { get; set; } = "CreatedAt"; + private SortDirection SortDirection { get; set; } = SortDirection.Descending; + + private async Task HandleSortChanged((string column, SortDirection direction) sort) + { + SortColumn = sort.column; + SortDirection = sort.direction; + await RefreshData(); + } + /// protected override async Task OnAfterRenderAsync(bool firstRender) { @@ -110,10 +116,11 @@ else query = query.Where(x => EF.Functions.Like(x.UserName!.ToLower(), "%" + SearchTerm.ToLower() + "%")); } - TotalRecords = await query.CountAsync(); + // Apply sort. + query = ApplySort(query); + TotalRecords = await query.CountAsync(); var users = await query - .OrderBy(x => x.CreatedAt) .Skip((CurrentPage - 1) * PageSize) .Take(PageSize) .Select(u => new @@ -149,4 +156,63 @@ else IsLoading = false; StateHasChanged(); } + + /// + /// Apply sort to the query. + /// + private IQueryable ApplySort(IQueryable query) + { + // Apply sort. + switch (SortColumn) + { + case "Id": + query = SortDirection == SortDirection.Ascending + ? query.OrderBy(x => x.Id) + : query.OrderByDescending(x => x.Id); + break; + case "CreatedAt": + query = SortDirection == SortDirection.Ascending + ? query.OrderBy(x => x.CreatedAt) + : query.OrderByDescending(x => x.CreatedAt); + break; + case "UserName": + query = SortDirection == SortDirection.Ascending + ? query.OrderBy(x => x.UserName) + : query.OrderByDescending(x => x.UserName); + break; + case "VaultCount": + query = SortDirection == SortDirection.Ascending + ? query.OrderBy(x => x.Vaults.Count) + : query.OrderByDescending(x => x.Vaults.Count); + break; + case "EmailClaimCount": + query = SortDirection == SortDirection.Ascending + ? query.OrderBy(x => x.EmailClaims.Count) + : query.OrderByDescending(x => x.EmailClaims.Count); + break; + case "VaultStorageInKb": + query = SortDirection == SortDirection.Ascending + ? query.OrderBy(x => x.Vaults.Sum(v => v.FileSize)) + : query.OrderByDescending(x => x.Vaults.Sum(v => v.FileSize)); + break; + case "TwoFactorEnabled": + query = SortDirection == SortDirection.Ascending + ? query.OrderBy(x => x.TwoFactorEnabled) + : query.OrderByDescending(x => x.TwoFactorEnabled); + break; + case "LastVaultUpdate": + query = SortDirection == SortDirection.Ascending + ? query.OrderBy(x => x.Vaults.Max(v => v.CreatedAt)) + : query.OrderByDescending(x => x.Vaults.Max(v => v.CreatedAt)); + break; + default: + query = SortDirection == SortDirection.Ascending + ? query.OrderBy(x => x.Id) + : query.OrderByDescending(x => x.Id); + break; + } + + return query; + } + } diff --git a/src/AliasVault.Admin/Main/Pages/Users/View.razor b/src/AliasVault.Admin/Main/Pages/Users/View.razor index 9890718bd..f29b80d41 100644 --- a/src/AliasVault.Admin/Main/Pages/Users/View.razor +++ b/src/AliasVault.Admin/Main/Pages/Users/View.razor @@ -9,21 +9,17 @@ } else { + + + + + + +
-
- - -
-

View user

- -
-
-
@@ -40,20 +36,14 @@ else Authenticator key(s) active: @TwoFactorKeysCount @if (User.TwoFactorEnabled) { - + } else { if (TwoFactorKeysCount > 0) { - - + + } }
@@ -68,60 +58,60 @@ else - - - - - - - - - - - - + + + + + + + + + + + + - @{ - Vault? previousEntry = null; - } - @foreach (var entry in VaultList.OrderBy(e => e.UpdatedAt)) - { - - - - - - - - - - - - + @{ + Vault? previousEntry = null; + } + @foreach (var entry in VaultList.OrderBy(e => e.UpdatedAt)) + { + + + + + + + + + + + + - previousEntry = entry; - } + previousEntry = entry; + }
IDCreatedUpdatedFilesizeDB versionRevisionCredentialsEmail ClaimsStatusActions
IDCreatedUpdatedFilesizeDB versionRevisionCredentialsEmail ClaimsStatusActions
@entry.Id@entry.CreatedAt.ToString("yyyy-MM-dd HH:mm")@entry.UpdatedAt.ToString("yyyy-MM-dd HH:mm")@Math.Round((double)entry.FileSize / 1024, 1) MB@entry.Version@entry.RevisionNumber@entry.CredentialsCount@entry.EmailClaimsCount - @if (entry == LatestVault) - { - Current - } - @if (previousEntry != null && HasPasswordChanged(entry, previousEntry)) - { - Password Changed - } - - @if (entry != LatestVault) - { - - } -
@entry.Id@entry.CreatedAt.ToString("yyyy-MM-dd HH:mm")@entry.UpdatedAt.ToString("yyyy-MM-dd HH:mm")@Math.Round((double)entry.FileSize / 1024, 1) MB@entry.Version@entry.RevisionNumber@entry.CredentialsCount@entry.EmailClaimsCount + @if (entry == LatestVault) + { + Current + } + @if (previousEntry != null && HasPasswordChanged(entry, previousEntry)) + { + Password Changed + } + + @if (entry != LatestVault) + { + + } +
- +
@@ -148,7 +138,7 @@ else @entry.CreatedAt.ToString("yyyy-MM-dd HH:mm") @entry.ExpireDate.ToString("yyyy-MM-dd HH:mm") - + } @@ -219,11 +209,11 @@ else if (firstRender) { - await LoadEntryAsync(); + await RefreshData(); } } - private async Task LoadEntryAsync() + private async Task RefreshData() { IsLoading = true; StateHasChanged(); @@ -294,7 +284,7 @@ else { DbContext.AliasVaultUserRefreshTokens.Remove(token); await DbContext.SaveChangesAsync(); - await LoadEntryAsync(); + await RefreshData(); } } @@ -310,7 +300,7 @@ else { User.TwoFactorEnabled = true; await DbContext.SaveChangesAsync(); - await LoadEntryAsync(); + await RefreshData(); } } @@ -327,7 +317,7 @@ else { User.TwoFactorEnabled = false; await DbContext.SaveChangesAsync(); - await LoadEntryAsync(); + await RefreshData(); } } @@ -348,7 +338,7 @@ else .ForEachAsync(x => DbContext.UserTokens.Remove(x)); await DbContext.SaveChangesAsync(); - await LoadEntryAsync(); + await RefreshData(); } } @@ -397,7 +387,7 @@ Do you want to proceed with the restoration?")) { await DbContext.SaveChangesAsync(); // Reload the page. - await LoadEntryAsync(); + await RefreshData(); } } } diff --git a/src/AliasVault.Admin/Main/_Imports.razor b/src/AliasVault.Admin/Main/_Imports.razor index 50d9a4ba8..07ecb91bf 100644 --- a/src/AliasVault.Admin/Main/_Imports.razor +++ b/src/AliasVault.Admin/Main/_Imports.razor @@ -20,6 +20,8 @@ @using AliasVault.RazorComponents @using AliasVault.RazorComponents.Alerts @using AliasVault.RazorComponents.Buttons +@using AliasVault.RazorComponents.Headings +@using AliasVault.RazorComponents.Models @using AliasVault.Admin.Main.Models @using AliasVault.Admin.Main.Pages @using AliasVault.Admin.Services diff --git a/src/AliasVault.Admin/tailwind.config.js b/src/AliasVault.Admin/tailwind.config.js index d02ab3413..0f275c0da 100644 --- a/src/AliasVault.Admin/tailwind.config.js +++ b/src/AliasVault.Admin/tailwind.config.js @@ -4,6 +4,7 @@ module.exports = { './**/*.html', './**/*.razor', '../Shared/AliasVault.RazorComponents/**/*.razor', + '../Shared/AliasVault.RazorComponents/**/*.cs', ], safelist: [ 'w-64', diff --git a/src/AliasVault.Admin/wwwroot/css/tailwind.css b/src/AliasVault.Admin/wwwroot/css/tailwind.css index df534f23f..ea97227db 100644 --- a/src/AliasVault.Admin/wwwroot/css/tailwind.css +++ b/src/AliasVault.Admin/wwwroot/css/tailwind.css @@ -600,6 +600,10 @@ video { border-width: 0; } +.visible { + visibility: visible; +} + .invisible { visibility: hidden; } @@ -675,10 +679,6 @@ video { margin-bottom: 1rem; } -.mb-5 { - margin-bottom: 1.25rem; -} - .mb-6 { margin-bottom: 1.5rem; } @@ -687,10 +687,6 @@ video { margin-bottom: 2rem; } -.ml-1 { - margin-left: 0.25rem; -} - .ml-2 { margin-left: 0.5rem; } @@ -751,6 +747,14 @@ video { margin-top: 2rem; } +.ms-1 { + margin-inline-start: 0.25rem; +} + +.ml-1 { + margin-left: 0.25rem; +} + .line-clamp-1 { overflow: hidden; display: -webkit-box; @@ -822,6 +826,10 @@ video { height: 100%; } +.h-3 { + height: 0.75rem; +} + .w-1\/2 { width: 50%; } @@ -874,6 +882,10 @@ video { width: 100%; } +.w-3 { + width: 0.75rem; +} + .max-w-2xl { max-width: 42rem; } @@ -952,10 +964,6 @@ video { align-items: flex-start; } -.items-end { - align-items: flex-end; -} - .items-center { align-items: center; } @@ -988,6 +996,12 @@ video { margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); } +.space-x-3 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.75rem * var(--tw-space-x-reverse)); + margin-left: calc(0.75rem * calc(1 - var(--tw-space-x-reverse))); +} + .space-x-4 > :not([hidden]) ~ :not([hidden]) { --tw-space-x-reverse: 0; margin-right: calc(1rem * var(--tw-space-x-reverse)); @@ -1024,6 +1038,12 @@ video { margin-bottom: calc(1.5rem * var(--tw-space-y-reverse)); } +.space-y-3 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.75rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.75rem * var(--tw-space-y-reverse)); +} + .divide-y > :not([hidden]) ~ :not([hidden]) { --tw-divide-y-reverse: 0; border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); @@ -1542,6 +1562,11 @@ video { color: rgb(107 114 128 / var(--tw-text-opacity)); } +.text-gray-600 { + --tw-text-opacity: 1; + color: rgb(75 85 99 / var(--tw-text-opacity)); +} + .text-gray-700 { --tw-text-opacity: 1; color: rgb(55 65 81 / var(--tw-text-opacity)); @@ -1719,11 +1744,6 @@ video { background-color: rgb(154 93 38 / var(--tw-bg-opacity)); } -.hover\:bg-red-700:hover { - --tw-bg-opacity: 1; - background-color: rgb(185 28 28 / var(--tw-bg-opacity)); -} - .hover\:bg-red-800:hover { --tw-bg-opacity: 1; background-color: rgb(153 27 27 / var(--tw-bg-opacity)); @@ -1780,11 +1800,6 @@ video { box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); } -.focus\:ring-blue-300:focus { - --tw-ring-opacity: 1; - --tw-ring-color: rgb(147 197 253 / var(--tw-ring-opacity)); -} - .focus\:ring-blue-500:focus { --tw-ring-opacity: 1; --tw-ring-color: rgb(59 130 246 / var(--tw-ring-opacity)); @@ -1894,11 +1909,6 @@ video { background-color: rgb(214 131 56 / var(--tw-bg-opacity)); } -.dark\:bg-red-500:is(.dark *) { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); -} - .dark\:bg-red-600:is(.dark *) { --tw-bg-opacity: 1; background-color: rgb(220 38 38 / var(--tw-bg-opacity)); @@ -2047,11 +2057,6 @@ video { background-color: rgb(184 112 47 / var(--tw-bg-opacity)); } -.dark\:hover\:bg-red-600:hover:is(.dark *) { - --tw-bg-opacity: 1; - background-color: rgb(220 38 38 / var(--tw-bg-opacity)); -} - .dark\:hover\:bg-red-700:hover:is(.dark *) { --tw-bg-opacity: 1; background-color: rgb(185 28 28 / var(--tw-bg-opacity)); @@ -2122,11 +2127,6 @@ video { --tw-ring-color: rgb(153 27 27 / var(--tw-ring-opacity)); } -.dark\:focus\:ring-red-900:focus:is(.dark *) { - --tw-ring-opacity: 1; - --tw-ring-color: rgb(127 29 29 / var(--tw-ring-opacity)); -} - @media (min-width: 640px) { .sm\:flex { display: flex; @@ -2194,6 +2194,10 @@ video { margin-right: 1.5rem; } + .md\:inline { + display: inline; + } + .md\:flex { display: flex; } diff --git a/src/AliasVault.Client/Main/Components/Widgets/CreateNewIdentityWidget.razor b/src/AliasVault.Client/Main/Components/Widgets/CreateNewIdentityWidget.razor index 83268dda4..0eaa59288 100644 --- a/src/AliasVault.Client/Main/Components/Widgets/CreateNewIdentityWidget.razor +++ b/src/AliasVault.Client/Main/Components/Widgets/CreateNewIdentityWidget.razor @@ -5,7 +5,7 @@ @implements IAsyncDisposable @if (IsPopupVisible) @@ -13,7 +13,7 @@
-

Create New Identity

+

Create New Alias

diff --git a/src/AliasVault.Client/Main/Layout/Footer.razor b/src/AliasVault.Client/Main/Layout/Footer.razor index 2afbbeb5d..9dd9de3d1 100644 --- a/src/AliasVault.Client/Main/Layout/Footer.razor +++ b/src/AliasVault.Client/Main/Layout/Footer.razor @@ -23,7 +23,7 @@ @code { private static readonly string[] Quotes = [ - "Tip: Use the g+c (go create) keyboard shortcut to quickly create a new identity.", + "Tip: Use the g+c (go create) keyboard shortcut to quickly create a new alias.", "Tip: Use the g+f (go find) keyboard shortcut to focus the search field.", "Tip: Use the g+h (go home) keyboard shortcut to go to the homepage.", "Tip: Use the g+l (go lock) keyboard shortcut to lock the vault.", diff --git a/src/AliasVault.Client/Main/Pages/Credentials/AddEdit.razor b/src/AliasVault.Client/Main/Pages/Credentials/AddEdit.razor index 262b1adc9..efcb2592c 100644 --- a/src/AliasVault.Client/Main/Pages/Credentials/AddEdit.razor +++ b/src/AliasVault.Client/Main/Pages/Credentials/AddEdit.razor @@ -11,39 +11,15 @@ @inject IJSRuntime JSRuntime @implements IAsyncDisposable -@if (EditMode) -{ - Edit credentials -} -else { - Add credentials -} - -
-
- -
- @if (EditMode) - { -

Edit credentials

- } - else { -

Add credentials

- } -
- - -
-
- @if (EditMode) - { -

Edit the existing credentials entry below.

- } - else { -

Create a new credentials entry below.

- } -
-
+ + + Save Credentials + Cancel + + @if (Loading) { @@ -96,7 +72,7 @@ else

Login credentials

- +
@@ -252,7 +228,7 @@ else if (!EditMode) { - // When creating a new identity: start with focus on the service name input. + // When creating a new alias: start with focus on the service name input. await JsInteropService.FocusElementById("service-name"); } } diff --git a/src/AliasVault.Client/Main/Pages/Credentials/Delete.razor b/src/AliasVault.Client/Main/Pages/Credentials/Delete.razor index 6864ce3cf..3c737e5a5 100644 --- a/src/AliasVault.Client/Main/Pages/Credentials/Delete.razor +++ b/src/AliasVault.Client/Main/Pages/Credentials/Delete.razor @@ -4,15 +4,11 @@ Delete credentials entry -
-
- -
-

Delete credentials

-
-

You can delete a credentials entry below.

-
-
+ + @if (IsLoading) { @@ -55,8 +51,8 @@ else protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); - BreadcrumbItems.Add(new BreadcrumbItem { Url = "credentials/" + Id, DisplayName = "View Credentials Entry" }); - BreadcrumbItems.Add(new BreadcrumbItem { DisplayName = "Delete credentials" }); + BreadcrumbItems.Add(new BreadcrumbItem { Url = "credentials/" + Id, DisplayName = "View credentials entry" }); + BreadcrumbItems.Add(new BreadcrumbItem { DisplayName = "Delete credential" }); } /// diff --git a/src/AliasVault.Client/Main/Pages/Credentials/Home.razor b/src/AliasVault.Client/Main/Pages/Credentials/Home.razor index 757a28f78..2023e6336 100644 --- a/src/AliasVault.Client/Main/Pages/Credentials/Home.razor +++ b/src/AliasVault.Client/Main/Pages/Credentials/Home.razor @@ -4,16 +4,14 @@ Home -
-
- -
-

Credentials

- -
-

Find all of your credentials below.

-
-
+ + + + + @if (IsLoading) { diff --git a/src/AliasVault.Client/Main/Pages/Credentials/View.razor b/src/AliasVault.Client/Main/Pages/Credentials/View.razor index 61712752c..f2aa12008 100644 --- a/src/AliasVault.Client/Main/Pages/Credentials/View.razor +++ b/src/AliasVault.Client/Main/Pages/Credentials/View.razor @@ -10,24 +10,24 @@ } else { + + + + + + +
-
- - - -
- -
@@ -48,7 +48,7 @@ else } - @if (Alias.Attachments != null && Alias.Attachments.Count > 0) + @if (Alias.Attachments.Count > 0) { } diff --git a/src/AliasVault.Client/Main/Pages/Emails/Home.razor b/src/AliasVault.Client/Main/Pages/Emails/Home.razor index 453d6c789..84819127f 100644 --- a/src/AliasVault.Client/Main/Pages/Emails/Home.razor +++ b/src/AliasVault.Client/Main/Pages/Emails/Home.razor @@ -17,16 +17,14 @@ } -
-
- -
-

Emails

- -
-

Below you can find all recent emails sent to one of the email addresses used in your credentials.

-
-
+ + + + + @if (IsLoading) { @@ -36,7 +34,7 @@ else if (NoEmailClaims) {
-

You are not using any private email addresses (yet). Create a new identity and use a private email address supported by AliasVault. All emails received by these private email addresses will show up here.

+

You are not using any private email addresses (yet). Create a new alias and use a private email address supported by AliasVault. All emails received by these private email addresses will show up here.

} diff --git a/src/AliasVault.Client/Main/Pages/MainBase.cs b/src/AliasVault.Client/Main/Pages/MainBase.cs index 4dcff94ab..b99dae4de 100644 --- a/src/AliasVault.Client/Main/Pages/MainBase.cs +++ b/src/AliasVault.Client/Main/Pages/MainBase.cs @@ -9,6 +9,7 @@ namespace AliasVault.Client.Main.Pages; using AliasVault.Client.Services; using AliasVault.Client.Services.Auth; +using AliasVault.RazorComponents.Models; using Blazored.LocalStorage; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Authorization; diff --git a/src/AliasVault.Client/Main/Pages/Settings/General.razor b/src/AliasVault.Client/Main/Pages/Settings/General.razor index c359c0279..e651985f7 100644 --- a/src/AliasVault.Client/Main/Pages/Settings/General.razor +++ b/src/AliasVault.Client/Main/Pages/Settings/General.razor @@ -3,13 +3,11 @@ General settings -
-
- -

General settings

-

Configure general AliasVault settings.

-
-
+ +

Email Settings

diff --git a/src/AliasVault.Client/Main/Pages/Settings/Security/ChangePassword.razor b/src/AliasVault.Client/Main/Pages/Settings/Security/ChangePassword.razor index 21f5adc71..52b8b9e65 100644 --- a/src/AliasVault.Client/Main/Pages/Settings/Security/ChangePassword.razor +++ b/src/AliasVault.Client/Main/Pages/Settings/Security/ChangePassword.razor @@ -11,7 +11,7 @@
-

Change password

+

Change password

Changing your master password also changes the vault encryption keys. It is advised to periodically change your master password to keep your vaults secure.

diff --git a/src/AliasVault.Client/Main/Pages/Settings/Security/Components/ActiveSessionsSection.razor b/src/AliasVault.Client/Main/Pages/Settings/Security/Components/ActiveSessionsSection.razor index 0de9e5ded..8aa81b54f 100644 --- a/src/AliasVault.Client/Main/Pages/Settings/Security/Components/ActiveSessionsSection.razor +++ b/src/AliasVault.Client/Main/Pages/Settings/Security/Components/ActiveSessionsSection.razor @@ -36,8 +36,7 @@ @session.CreatedAt.ToLocalTime().ToString("g") @session.ExpireDate.ToLocalTime().ToString("g") - + } diff --git a/src/AliasVault.Client/Main/Pages/Settings/Security/Disable2Fa.razor b/src/AliasVault.Client/Main/Pages/Settings/Security/Disable2Fa.razor index d8ed9bb3a..a65862c40 100644 --- a/src/AliasVault.Client/Main/Pages/Settings/Security/Disable2Fa.razor +++ b/src/AliasVault.Client/Main/Pages/Settings/Security/Disable2Fa.razor @@ -13,7 +13,7 @@ else
-

Disable two-factor authentication

+

Disable two-factor authentication

Disabling two-factor authentication means you will be able to login with only your password.

diff --git a/src/AliasVault.Client/Main/Pages/Settings/Security/Enable2Fa.razor b/src/AliasVault.Client/Main/Pages/Settings/Security/Enable2Fa.razor index 5f3b4a35c..2c360051f 100644 --- a/src/AliasVault.Client/Main/Pages/Settings/Security/Enable2Fa.razor +++ b/src/AliasVault.Client/Main/Pages/Settings/Security/Enable2Fa.razor @@ -8,7 +8,7 @@
-

Enable two-factor authentication

+

Enable two-factor authentication

Enable two-factor authentication to increase the security of your vaults.

diff --git a/src/AliasVault.Client/Main/Pages/Settings/Security/Security.razor b/src/AliasVault.Client/Main/Pages/Settings/Security/Security.razor index 2c1a72e3e..3ac386699 100644 --- a/src/AliasVault.Client/Main/Pages/Settings/Security/Security.razor +++ b/src/AliasVault.Client/Main/Pages/Settings/Security/Security.razor @@ -4,16 +4,14 @@ Security settings -
-
- -
-

Security settings

- -
-

Configure security settings.

-
-
+ + + + + diff --git a/src/AliasVault.Client/Main/Pages/Settings/Vault.razor b/src/AliasVault.Client/Main/Pages/Settings/Vault.razor index 550a687eb..1e7c671d0 100644 --- a/src/AliasVault.Client/Main/Pages/Settings/Vault.razor +++ b/src/AliasVault.Client/Main/Pages/Settings/Vault.razor @@ -5,28 +5,20 @@ Vault settings -
-
- -
-

Vault settings

-
-

On this page you can configure your vault settings.

-
-
+ +

Export vault

- +
- +
@@ -70,13 +62,13 @@ else if (!string.IsNullOrEmpty(ImportSuccessMessage)) { try { - // Decode the base64 string to a byte array + // Decode the base64 string to a byte array. byte[] fileBytes = Convert.FromBase64String(await DbService.ExportSqliteToBase64Async()); - // Create a memory stream from the byte array + // Create a memory stream from the byte array. using (MemoryStream memoryStream = new MemoryStream(fileBytes)) { - // Invoke JavaScript to initiate the download + // Invoke JavaScript to initiate the download. await JsInteropService.DownloadFileFromStream("aliasvault-client.sqlite", memoryStream.ToArray()); } } @@ -94,10 +86,10 @@ else if (!string.IsNullOrEmpty(ImportSuccessMessage)) var csvBytes = CsvImportExport.CredentialCsvService.ExportCredentialsToCsv(credentials); - // Create a memory stream from the byte array + // Create a memory stream from the byte array. using (MemoryStream memoryStream = new MemoryStream(csvBytes)) { - // Invoke JavaScript to initiate the download + // Invoke JavaScript to initiate the download. await JsInteropService.DownloadFileFromStream("aliasvault-client.csv", memoryStream.ToArray()); } } @@ -127,7 +119,7 @@ else if (!string.IsNullOrEmpty(ImportSuccessMessage)) var fileContent = System.Text.Encoding.UTF8.GetString(buffer); var importedCredentials = CsvImportExport.CredentialCsvService.ImportCredentialsFromCsv(fileContent); - // Loop through the imported credentials and actually add them to the database + // Loop through the imported credentials and actually add them to the database. foreach (var importedCredential in importedCredentials) { await CredentialService.InsertEntryAsync(importedCredential, false); diff --git a/src/AliasVault.Client/Main/Pages/Test/Test1.razor b/src/AliasVault.Client/Main/Pages/Test/Test1.razor index cd4408a0e..e82467dd0 100644 --- a/src/AliasVault.Client/Main/Pages/Test/Test1.razor +++ b/src/AliasVault.Client/Main/Pages/Test/Test1.razor @@ -4,15 +4,11 @@ Test webapi call 1 -
-
- -
-

Test webapi call 1

-
-

Test webapi call 1.

-
-
+ + @if (IsLoading) { diff --git a/src/AliasVault.Client/Main/Pages/Test/Test2.razor b/src/AliasVault.Client/Main/Pages/Test/Test2.razor index d6dfac6ce..a633ec842 100644 --- a/src/AliasVault.Client/Main/Pages/Test/Test2.razor +++ b/src/AliasVault.Client/Main/Pages/Test/Test2.razor @@ -4,15 +4,11 @@ Test webapi call 2 -
-
- -
-

Test webapi call 2

-
-

Test webapi call 2.

-
-
+ + @if (IsLoading) { diff --git a/src/AliasVault.Client/Main/Pages/Welcome.razor b/src/AliasVault.Client/Main/Pages/Welcome.razor index edd2000ea..05eee94e8 100644 --- a/src/AliasVault.Client/Main/Pages/Welcome.razor +++ b/src/AliasVault.Client/Main/Pages/Welcome.razor @@ -1,7 +1,7 @@ @page "/welcome" @inherits MainBase -
+

Welcome to AliasVault

It looks like you are new here. The instructions on this page will help to get you started.

@@ -11,7 +11,7 @@

How do I use AliasVault?

-

Create a random identity with an associated email address. To get started, simply click the "+ New Identity" button in the top right corner.

+

Create a random identity with an associated email address. To get started, simply click the "+ New Alias" button in the top right corner.

What is the purpose of AliasVault?

@@ -28,7 +28,7 @@

Get Started Now

- Go ahead and create a new login by clicking "+ New Identity" in the top right. Or explore these options: + Go ahead and create a new login by clicking "+ New Alias" in the top right. Or explore these options:

diff --git a/src/AliasVault.Client/_Imports.razor b/src/AliasVault.Client/_Imports.razor index 6793fed06..c52d02bb5 100644 --- a/src/AliasVault.Client/_Imports.razor +++ b/src/AliasVault.Client/_Imports.razor @@ -27,6 +27,8 @@ @using AliasVault.RazorComponents @using AliasVault.RazorComponents.Alerts @using AliasVault.RazorComponents.Buttons +@using AliasVault.RazorComponents.Headings +@using AliasVault.RazorComponents.Models @using Microsoft.AspNetCore.Components.Authorization @using Microsoft.AspNetCore.Components.WebAssembly.Authentication @using Blazored.LocalStorage diff --git a/src/AliasVault.Client/tailwind.config.js b/src/AliasVault.Client/tailwind.config.js index d02ab3413..4b67e5747 100644 --- a/src/AliasVault.Client/tailwind.config.js +++ b/src/AliasVault.Client/tailwind.config.js @@ -3,6 +3,7 @@ module.exports = { content: [ './**/*.html', './**/*.razor', + '../Shared/AliasVault.RazorComponents/**/*.cs', '../Shared/AliasVault.RazorComponents/**/*.razor', ], safelist: [ diff --git a/src/AliasVault.Client/wwwroot/css/tailwind.css b/src/AliasVault.Client/wwwroot/css/tailwind.css index b88d6885c..4b0fb961d 100644 --- a/src/AliasVault.Client/wwwroot/css/tailwind.css +++ b/src/AliasVault.Client/wwwroot/css/tailwind.css @@ -554,40 +554,6 @@ video { --tw-contain-style: ; } -.container { - width: 100%; -} - -@media (min-width: 640px) { - .container { - max-width: 640px; - } -} - -@media (min-width: 768px) { - .container { - max-width: 768px; - } -} - -@media (min-width: 1024px) { - .container { - max-width: 1024px; - } -} - -@media (min-width: 1280px) { - .container { - max-width: 1280px; - } -} - -@media (min-width: 1536px) { - .container { - max-width: 1536px; - } -} - .sr-only { position: absolute; width: 1px; @@ -600,6 +566,10 @@ video { border-width: 0; } +.visible { + visibility: visible; +} + .static { position: static; } @@ -717,10 +687,6 @@ video { margin-bottom: 1rem; } -.mb-5 { - margin-bottom: 1.25rem; -} - .mb-6 { margin-bottom: 1.5rem; } @@ -797,6 +763,10 @@ video { margin-top: 2rem; } +.ml-1 { + margin-left: 0.25rem; +} + .line-clamp-2 { overflow: hidden; display: -webkit-box; @@ -1072,6 +1042,12 @@ video { margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse))); } +.space-x-3 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.75rem * var(--tw-space-x-reverse)); + margin-left: calc(0.75rem * calc(1 - var(--tw-space-x-reverse))); +} + .space-x-4 > :not([hidden]) ~ :not([hidden]) { --tw-space-x-reverse: 0; margin-right: calc(1rem * var(--tw-space-x-reverse)); @@ -1517,10 +1493,6 @@ video { padding-right: 0.75rem; } -.pt-10 { - padding-top: 2.5rem; -} - .pt-16 { padding-top: 4rem; } @@ -1913,11 +1885,6 @@ video { background-color: rgb(220 38 38 / var(--tw-bg-opacity)); } -.hover\:bg-red-700:hover { - --tw-bg-opacity: 1; - background-color: rgb(185 28 28 / var(--tw-bg-opacity)); -} - .hover\:bg-red-800:hover { --tw-bg-opacity: 1; background-color: rgb(153 27 27 / var(--tw-bg-opacity)); @@ -2119,11 +2086,6 @@ video { background-color: rgb(17 24 39 / var(--tw-bg-opacity)); } -.dark\:bg-green-500:is(.dark *) { - --tw-bg-opacity: 1; - background-color: rgb(34 197 94 / var(--tw-bg-opacity)); -} - .dark\:bg-green-600:is(.dark *) { --tw-bg-opacity: 1; background-color: rgb(22 163 74 / var(--tw-bg-opacity)); @@ -2139,11 +2101,6 @@ video { background-color: rgb(214 131 56 / var(--tw-bg-opacity)); } -.dark\:bg-red-500:is(.dark *) { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); -} - .dark\:bg-red-600:is(.dark *) { --tw-bg-opacity: 1; background-color: rgb(220 38 38 / var(--tw-bg-opacity)); @@ -2267,11 +2224,6 @@ video { background-color: rgb(55 65 81 / var(--tw-bg-opacity)); } -.dark\:hover\:bg-green-600:hover:is(.dark *) { - --tw-bg-opacity: 1; - background-color: rgb(22 163 74 / var(--tw-bg-opacity)); -} - .dark\:hover\:bg-green-700:hover:is(.dark *) { --tw-bg-opacity: 1; background-color: rgb(21 128 61 / var(--tw-bg-opacity)); @@ -2287,11 +2239,6 @@ video { background-color: rgb(184 112 47 / var(--tw-bg-opacity)); } -.dark\:hover\:bg-red-600:hover:is(.dark *) { - --tw-bg-opacity: 1; - background-color: rgb(220 38 38 / var(--tw-bg-opacity)); -} - .dark\:hover\:bg-red-700:hover:is(.dark *) { --tw-bg-opacity: 1; background-color: rgb(185 28 28 / var(--tw-bg-opacity)); @@ -2569,11 +2516,6 @@ video { .lg\:gap-4 { gap: 1rem; } - - .lg\:px-0 { - padding-left: 0px; - padding-right: 0px; - } } @media (min-width: 1280px) { diff --git a/src/Databases/AliasServerDb/AliasServerDbContext.cs b/src/Databases/AliasServerDb/AliasServerDbContext.cs index 31e71426b..9309f30ab 100644 --- a/src/Databases/AliasServerDb/AliasServerDbContext.cs +++ b/src/Databases/AliasServerDb/AliasServerDbContext.cs @@ -133,17 +133,20 @@ public class AliasServerDbContext : WorkerStatusDbContext, IDataProtectionKeyCon protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); - foreach (var entity in modelBuilder.Model.GetEntityTypes()) - { - foreach (var property in entity.GetProperties()) - { - // NOTE: This is a workaround for SQLite. Add conditional check if SQLite is used. - // NOTE: SQL server doesn't need this override. - // SQLite does not support varchar(max) so we use TEXT. - if (property.ClrType == typeof(string) && property.GetMaxLength() == null) + // NOTE: This is a workaround for SQLite. Add conditional check if SQLite is used. + // NOTE: SQL server doesn't need this override. + if (Database.IsSqlite()) + { + foreach (var entity in modelBuilder.Model.GetEntityTypes()) + { + foreach (var property in entity.GetProperties()) { - property.SetColumnType("TEXT"); + // SQLite does not support varchar(max) so we use TEXT. + if (property.ClrType == typeof(string) && property.GetMaxLength() == null) + { + property.SetColumnType("TEXT"); + } } } } diff --git a/src/Databases/AliasServerDb/Log.cs b/src/Databases/AliasServerDb/Log.cs index 6e0370660..90a22146b 100644 --- a/src/Databases/AliasServerDb/Log.cs +++ b/src/Databases/AliasServerDb/Log.cs @@ -54,7 +54,7 @@ public class Log /// /// Gets or sets the timestamp of the log entry. /// - public DateTimeOffset TimeStamp { get; set; } + public DateTime TimeStamp { get; set; } /// /// Gets or sets the exception associated with the log entry. diff --git a/src/Databases/AliasServerDb/Migrations/20241007153012_DatetimeOffsetToDateTime.Designer.cs b/src/Databases/AliasServerDb/Migrations/20241007153012_DatetimeOffsetToDateTime.Designer.cs new file mode 100644 index 000000000..2467b8299 --- /dev/null +++ b/src/Databases/AliasServerDb/Migrations/20241007153012_DatetimeOffsetToDateTime.Designer.cs @@ -0,0 +1,831 @@ +// +using System; +using AliasServerDb; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace AliasServerDb.Migrations +{ + [DbContext(typeof(AliasServerDbContext))] + [Migration("20241007153012_DatetimeOffsetToDateTime")] + partial class DatetimeOffsetToDateTime + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.8") + .HasAnnotation("Proxies:ChangeTracking", false) + .HasAnnotation("Proxies:CheckEquality", false) + .HasAnnotation("Proxies:LazyLoading", true); + + modelBuilder.Entity("AliasServerDb.AdminRole", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NormalizedName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("AdminRoles"); + }); + + modelBuilder.Entity("AliasServerDb.AdminUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("ConcurrencyStamp") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("LastPasswordChanged") + .HasColumnType("TEXT"); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("NormalizedEmail") + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("AdminUsers"); + }); + + modelBuilder.Entity("AliasServerDb.AliasVaultRole", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("NormalizedName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("AliasVaultRoles"); + }); + + modelBuilder.Entity("AliasServerDb.AliasVaultUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("ConcurrencyStamp") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("NormalizedEmail") + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasColumnType("TEXT"); + + b.Property("PasswordChangedAt") + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UserName") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("AliasVaultUsers"); + }); + + modelBuilder.Entity("AliasServerDb.AliasVaultUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DeviceIdentifier") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("ExpireDate") + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("IpAddress") + .HasMaxLength(45) + .HasColumnType("TEXT"); + + b.Property("PreviousTokenValue") + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AliasVaultUserRefreshTokens"); + }); + + modelBuilder.Entity("AliasServerDb.AuthLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("AdditionalInfo") + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("Browser") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("Country") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("DeviceType") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("EventType") + .HasColumnType("nvarchar(50)"); + + b.Property("FailureReason") + .HasColumnType("INTEGER"); + + b.Property("IpAddress") + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("IsSuccess") + .HasColumnType("INTEGER"); + + b.Property("IsSuspiciousActivity") + .HasColumnType("INTEGER"); + + b.Property("OperatingSystem") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("RequestPath") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("Timestamp") + .HasColumnType("TEXT"); + + b.Property("UserAgent") + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex(new[] { "EventType" }, "IX_EventType"); + + b.HasIndex(new[] { "IpAddress" }, "IX_IpAddress"); + + b.HasIndex(new[] { "Timestamp" }, "IX_Timestamp"); + + b.HasIndex(new[] { "Username", "IsSuccess", "Timestamp" }, "IX_Username_IsSuccess_Timestamp") + .IsDescending(false, false, true); + + b.HasIndex(new[] { "Username", "Timestamp" }, "IX_Username_Timestamp") + .IsDescending(false, true); + + b.ToTable("AuthLogs"); + }); + + modelBuilder.Entity("AliasServerDb.Email", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Date") + .HasColumnType("TEXT"); + + b.Property("DateSystem") + .HasColumnType("TEXT"); + + b.Property("EncryptedSymmetricKey") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("From") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FromDomain") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FromLocal") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("MessageHtml") + .HasColumnType("TEXT"); + + b.Property("MessagePlain") + .HasColumnType("TEXT"); + + b.Property("MessagePreview") + .HasColumnType("TEXT"); + + b.Property("MessageSource") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PushNotificationSent") + .HasColumnType("INTEGER"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("To") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ToDomain") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ToLocal") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UserEncryptionKeyId") + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("Visible") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Date"); + + b.HasIndex("DateSystem"); + + b.HasIndex("PushNotificationSent"); + + b.HasIndex("ToLocal"); + + b.HasIndex("UserEncryptionKeyId"); + + b.HasIndex("Visible"); + + b.ToTable("Emails"); + }); + + modelBuilder.Entity("AliasServerDb.EmailAttachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Bytes") + .IsRequired() + .HasColumnType("BLOB"); + + b.Property("Date") + .HasColumnType("TEXT"); + + b.Property("EmailId") + .HasColumnType("INTEGER"); + + b.Property("Filename") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Filesize") + .HasColumnType("INTEGER"); + + b.Property("MimeType") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EmailId"); + + b.ToTable("EmailAttachments"); + }); + + modelBuilder.Entity("AliasServerDb.Log", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Application") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Exception") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Level") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("LogEvent") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("LogEvent"); + + b.Property("Message") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("MessageTemplate") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Properties") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("SourceContext") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TimeStamp") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Application"); + + b.HasIndex("TimeStamp"); + + b.ToTable("Logs", (string)null); + }); + + modelBuilder.Entity("AliasServerDb.UserEmailClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Address") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("AddressDomain") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("AddressLocal") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Address") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("UserEmailClaims"); + }); + + modelBuilder.Entity("AliasServerDb.UserEncryptionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("IsPrimary") + .HasColumnType("INTEGER"); + + b.Property("PublicKey") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserEncryptionKeys"); + }); + + modelBuilder.Entity("AliasServerDb.Vault", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CredentialsCount") + .HasColumnType("INTEGER"); + + b.Property("EmailClaimsCount") + .HasColumnType("INTEGER"); + + b.Property("EncryptionSettings") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("EncryptionType") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FileSize") + .HasColumnType("INTEGER"); + + b.Property("RevisionNumber") + .HasColumnType("INTEGER"); + + b.Property("Salt") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.Property("VaultBlob") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Verifier") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("TEXT"); + + b.Property("Version") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Vaults"); + }); + + modelBuilder.Entity("AliasVault.WorkerStatus.Database.WorkerServiceStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("CurrentStatus") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("DesiredStatus") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Heartbeat") + .HasColumnType("TEXT"); + + b.Property("ServiceName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar"); + + b.HasKey("Id"); + + b.ToTable("WorkerServiceStatuses"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FriendlyName") + .HasColumnType("TEXT"); + + b.Property("Xml") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("RoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("UserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.ToTable("UserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.ToTable("UserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("UserTokens", (string)null); + }); + + modelBuilder.Entity("AliasServerDb.AliasVaultUserRefreshToken", b => + { + b.HasOne("AliasServerDb.AliasVaultUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("AliasServerDb.Email", b => + { + b.HasOne("AliasServerDb.UserEncryptionKey", "EncryptionKey") + .WithMany("Emails") + .HasForeignKey("UserEncryptionKeyId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("EncryptionKey"); + }); + + modelBuilder.Entity("AliasServerDb.EmailAttachment", b => + { + b.HasOne("AliasServerDb.Email", "Email") + .WithMany("Attachments") + .HasForeignKey("EmailId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Email"); + }); + + modelBuilder.Entity("AliasServerDb.UserEmailClaim", b => + { + b.HasOne("AliasServerDb.AliasVaultUser", "User") + .WithMany("EmailClaims") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("AliasServerDb.UserEncryptionKey", b => + { + b.HasOne("AliasServerDb.AliasVaultUser", "User") + .WithMany("EncryptionKeys") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("AliasServerDb.Vault", b => + { + b.HasOne("AliasServerDb.AliasVaultUser", "User") + .WithMany("Vaults") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("AliasServerDb.AliasVaultUser", b => + { + b.Navigation("EmailClaims"); + + b.Navigation("EncryptionKeys"); + + b.Navigation("Vaults"); + }); + + modelBuilder.Entity("AliasServerDb.Email", b => + { + b.Navigation("Attachments"); + }); + + modelBuilder.Entity("AliasServerDb.UserEncryptionKey", b => + { + b.Navigation("Emails"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Databases/AliasServerDb/Migrations/20241007153012_DatetimeOffsetToDateTime.cs b/src/Databases/AliasServerDb/Migrations/20241007153012_DatetimeOffsetToDateTime.cs new file mode 100644 index 000000000..c835ed734 --- /dev/null +++ b/src/Databases/AliasServerDb/Migrations/20241007153012_DatetimeOffsetToDateTime.cs @@ -0,0 +1,23 @@ +// +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AliasServerDb.Migrations +{ + /// + public partial class DatetimeOffsetToDateTime : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/src/Databases/AliasServerDb/Migrations/AliasServerDbContextModelSnapshot.cs b/src/Databases/AliasServerDb/Migrations/AliasServerDbContextModelSnapshot.cs index 1a09bd173..a31749918 100644 --- a/src/Databases/AliasServerDb/Migrations/AliasServerDbContextModelSnapshot.cs +++ b/src/Databases/AliasServerDb/Migrations/AliasServerDbContextModelSnapshot.cs @@ -452,7 +452,7 @@ namespace AliasServerDb.Migrations .IsRequired() .HasColumnType("TEXT"); - b.Property("TimeStamp") + b.Property("TimeStamp") .HasColumnType("TEXT"); b.HasKey("Id"); diff --git a/src/Shared/AliasVault.RazorComponents/AliasVault.RazorComponents.csproj b/src/Shared/AliasVault.RazorComponents/AliasVault.RazorComponents.csproj index ae089f7b6..0d2a96e6b 100644 --- a/src/Shared/AliasVault.RazorComponents/AliasVault.RazorComponents.csproj +++ b/src/Shared/AliasVault.RazorComponents/AliasVault.RazorComponents.csproj @@ -18,7 +18,7 @@ - + diff --git a/src/Shared/AliasVault.RazorComponents/Buttons/Button.razor b/src/Shared/AliasVault.RazorComponents/Buttons/Button.razor index a89ebbdbc..d49a123f4 100644 --- a/src/Shared/AliasVault.RazorComponents/Buttons/Button.razor +++ b/src/Shared/AliasVault.RazorComponents/Buttons/Button.razor @@ -1,4 +1,5 @@ - + +@code { + /// + /// The event to call in the parent when the cancel button is clicked. + /// + [Parameter] + public EventCallback OnClick { get; set; } + + /// + /// The content to be displayed inside the button. + /// + [Parameter] + public RenderFragment? ChildContent { get; set; } + + /// + /// Handles the button click event. + /// + private async Task HandleClick() + { + await OnClick.InvokeAsync(); + } +} diff --git a/src/Shared/AliasVault.RazorComponents/Buttons/ConfirmButton.razor b/src/Shared/AliasVault.RazorComponents/Buttons/ConfirmButton.razor new file mode 100644 index 000000000..273d11c06 --- /dev/null +++ b/src/Shared/AliasVault.RazorComponents/Buttons/ConfirmButton.razor @@ -0,0 +1,26 @@ + + +@code { + /// + /// The event to call in the parent when the confirm button is clicked. + /// + [Parameter] + public EventCallback OnClick { get; set; } + + /// + /// The content to be displayed inside the button. + /// + [Parameter] + public RenderFragment? ChildContent { get; set; } + + /// + /// Handles the button click event. + /// + private async Task HandleClick() + { + await OnClick.InvokeAsync(); + } +} diff --git a/src/Shared/AliasVault.RazorComponents/Buttons/DeleteButton.razor b/src/Shared/AliasVault.RazorComponents/Buttons/DeleteButton.razor index 0452707e2..b6d115637 100644 --- a/src/Shared/AliasVault.RazorComponents/Buttons/DeleteButton.razor +++ b/src/Shared/AliasVault.RazorComponents/Buttons/DeleteButton.razor @@ -1,4 +1,5 @@ + +@code { + /// + /// The content to be displayed inside the button. + /// + [Parameter] + public RenderFragment? ChildContent { get; set; } +} diff --git a/src/AliasVault.Client/Main/Components/Layout/Breadcrumb.razor b/src/Shared/AliasVault.RazorComponents/Headings/Breadcrumb.razor similarity index 97% rename from src/AliasVault.Client/Main/Components/Layout/Breadcrumb.razor rename to src/Shared/AliasVault.RazorComponents/Headings/Breadcrumb.razor index e40d4516d..ef3a4b6a5 100644 --- a/src/AliasVault.Client/Main/Components/Layout/Breadcrumb.razor +++ b/src/Shared/AliasVault.RazorComponents/Headings/Breadcrumb.razor @@ -1,6 +1,4 @@ -@inherits ComponentBase - -