Add vault decrypt error message (#74)

This commit is contained in:
Leendert de Borst
2024-07-08 11:25:50 +02:00
parent c749853870
commit 96a7fbaf3b
11 changed files with 794 additions and 33 deletions

View File

@@ -70,6 +70,9 @@
<Link>.dockerignore</Link>
</Content>
<AdditionalFiles Include="..\stylecop.json" Link="stylecop.json" />
<Content Update="wwwroot\appsettings.Development.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>

View File

@@ -34,13 +34,13 @@ else
private async void OnDatabaseStateChanged(object? sender, DbServiceState.DatabaseState newState)
{
await InvokeAsync(StateHasChanged);
if (newState.Status == DbServiceState.DatabaseStatus.Saving)
if (newState.Status == DbServiceState.DatabaseStatus.SavingToServer)
{
// Show loading indicator for at least 0.5 seconds even if the save operation is faster.
Message = "Saving...";
await ShowLoadingIndicatorAsync();
}
else if (newState.Status == DbServiceState.DatabaseStatus.Loading)
else if (newState.Status == DbServiceState.DatabaseStatus.LoadingFromServer)
{
Message = "Loading...";
await ShowLoadingIndicatorAsync();

View File

@@ -2,9 +2,7 @@
@implements IDisposable
@inject DbService DbService
@inject AuthenticationStateProvider AuthStateProvider
@inject NavigationManager NavigationManager
@using Microsoft.AspNetCore.Components.Authorization
@using AliasVault.WebApp.Providers
@using AliasVault.WebApp.Main.Layout.StatusMessages
<CascadingAuthenticationState>
<AuthorizeView>
@@ -19,20 +17,13 @@
</main>
<Footer />
}
else if(IsDbDecryptionError)
{
<ErrorVaultDecrypt />
}
else
{
<div class="fixed inset-0 overflow-y-auto h-full w-full flex items-center justify-center">
<div class="relative p-8 bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-md mx-auto">
<div class="text-center">
<svg class="mx-auto animate-spin h-12 w-12 text-primary-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
<h2 class="mt-4 text-xl font-semibold text-gray-900 dark:text-white">Vault decryption in progress</h2>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">Please wait while your vault is initialized. This may take a moment.</p>
</div>
</div>
</div>
<VaultDecryptionProgress />
}
</div>
</div>
@@ -47,6 +38,7 @@
@code {
private bool IsDbInitialized { get; set; } = false;
private bool IsDbDecryptionError { get; set; } = false;
private const int MinimumLoadingTimeMs = 800;
[CascadingParameter] private Task<AuthenticationState>? AuthState { get; set; }
@@ -55,6 +47,10 @@
{
await base.OnInitializedAsync();
// Reset local state
IsDbInitialized = false;
IsDbDecryptionError = false;
DbService.GetState().StateChanged += OnDatabaseStateChanged;
AuthStateProvider.AuthenticationStateChanged += OnAuthenticationStateChanged;
@@ -90,11 +86,16 @@
{
await InitializeDatabaseWithProgress();
}
else if (currentState.Status == DbServiceState.DatabaseStatus.Initialized)
else if (currentState.Status == DbServiceState.DatabaseStatus.Ready)
{
IsDbInitialized = true;
StateHasChanged();
}
else if (currentState.Status == DbServiceState.DatabaseStatus.DecryptionFailed)
{
IsDbDecryptionError = true;
StateHasChanged();
}
}
private async void OnDatabaseStateChanged(object? sender, DbServiceState.DatabaseState newState)
@@ -103,10 +104,14 @@
{
await InitializeDatabaseWithProgress();
}
else if (newState.Status == DbServiceState.DatabaseStatus.Initialized)
else if (newState.Status == DbServiceState.DatabaseStatus.Ready)
{
IsDbInitialized = true;
}
else if (newState.Status == DbServiceState.DatabaseStatus.DecryptionFailed)
{
IsDbDecryptionError = true;
}
StateHasChanged();
}
@@ -127,7 +132,7 @@
await Task.Delay(MinimumLoadingTimeMs - elapsedMs);
}
IsDbInitialized = true;
await CheckAndInitializeDatabase();
StateHasChanged();
}

View File

@@ -0,0 +1,8 @@
<div class="fixed inset-0 overflow-y-auto h-full w-full flex items-center justify-center">
<div class="relative p-8 bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-md mx-auto">
<div class="text-center">
<h2 class="mt-4 text-xl font-semibold text-gray-900 dark:text-white">Vault decryption error.</h2>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">An error occured while locally decrypting your vault. Your data is not accessible at this moment. Please try again (later) or contact support.</p>
</div>
</div>
</div>

View File

@@ -0,0 +1,12 @@
<div class="fixed inset-0 overflow-y-auto h-full w-full flex items-center justify-center">
<div class="relative p-8 bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-md mx-auto">
<div class="text-center">
<svg class="mx-auto animate-spin h-12 w-12 text-primary-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
<h2 class="mt-4 text-xl font-semibold text-gray-900 dark:text-white">Vault decryption in progress</h2>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">Please wait while your vault is initialized. This may take a moment.</p>
</div>
</div>
</div>

View File

@@ -13,6 +13,7 @@ using AliasClientDb;
using AliasVault.Shared.Models.WebApi;
using AliasVault.WebApp.Services.Auth;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.JSInterop;
/// <summary>
@@ -72,22 +73,22 @@ public class DbService : IDisposable
return;
}
_state.UpdateState(DbServiceState.DatabaseStatus.Loading);
_state.UpdateState(DbServiceState.DatabaseStatus.LoadingFromServer);
// Ensure the in-memory database representation is created and has the necessary tables.
await _dbContext.Database.EnsureCreatedAsync();
await _dbContext.Database.MigrateAsync();
// Attempt to fill the local database with a previously saved database stored on the server.
var loaded = await LoadDatabaseFromServerAsync();
if (loaded)
{
_isSuccessfullyInitialized = true;
_state.UpdateState(DbServiceState.DatabaseStatus.Initialized);
_state.UpdateState(DbServiceState.DatabaseStatus.Ready);
Console.WriteLine("Database succesfully loaded from server.");
}
else
{
_state.UpdateState(DbServiceState.DatabaseStatus.Error);
_state.UpdateState(DbServiceState.DatabaseStatus.DecryptionFailed);
Console.WriteLine("Failed to load database from server.");
}
}
@@ -122,7 +123,7 @@ public class DbService : IDisposable
public async Task SaveDatabaseAsync()
{
// Set the initial state of the database service.
_state.UpdateState(DbServiceState.DatabaseStatus.Saving);
_state.UpdateState(DbServiceState.DatabaseStatus.SavingToServer);
// Save the actual dbContext.
await _dbContext.SaveChangesAsync();
@@ -137,12 +138,12 @@ public class DbService : IDisposable
if (success)
{
Console.WriteLine("Database succesfully saved to server.");
_state.UpdateState(DbServiceState.DatabaseStatus.Initialized);
_state.UpdateState(DbServiceState.DatabaseStatus.Ready);
}
else
{
Console.WriteLine("Failed to save database to server.");
_state.UpdateState(DbServiceState.DatabaseStatus.Error);
_state.UpdateState(DbServiceState.DatabaseStatus.OperationError);
}
}
@@ -234,7 +235,7 @@ public class DbService : IDisposable
using (var command = _sqlConnection.CreateCommand())
{
// Drop all tables in the original database
// Empty all tables in the original database
command.CommandText = @"
SELECT 'DELETE FROM ' || name || ';'
FROM sqlite_master

View File

@@ -30,24 +30,29 @@ public class DbServiceState
Uninitialized,
/// <summary>
/// Database is initialized but no task is currently in progress.
/// Database failed to decrypt. No data is accessible.
/// </summary>
Initialized,
DecryptionFailed,
/// <summary>
/// Database is ready but no task is currently in progress.
/// </summary>
Ready,
/// <summary>
/// Database is loading from server.
/// </summary>
Loading,
LoadingFromServer,
/// <summary>
/// Database is saving to server.
/// </summary>
Saving,
SavingToServer,
/// <summary>
/// An error occurred during a database operation.
/// </summary>
Error,
OperationError,
}
/// <summary>

View File

@@ -0,0 +1,4 @@
{
"ApiUrl": "http://localhost:5092",
"UseDebugEncryptionKey": "false"
}

View File

@@ -0,0 +1,278 @@
// <auto-generated />
using System;
using AliasClientDb;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace AliasClientDb.Migrations
{
[DbContext(typeof(AliasClientDbContext))]
[Migration("20240708083723_100InitialMigration")]
partial class _100InitialMigration
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.6")
.HasAnnotation("Proxies:ChangeTracking", false)
.HasAnnotation("Proxies:CheckEquality", false)
.HasAnnotation("Proxies:LazyLoading", true);
modelBuilder.Entity("AliasClientDb.Alias", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<string>("AddressCity")
.HasMaxLength(255)
.HasColumnType("VARCHAR");
b.Property<string>("AddressCountry")
.HasMaxLength(255)
.HasColumnType("VARCHAR");
b.Property<string>("AddressState")
.HasMaxLength(255)
.HasColumnType("VARCHAR");
b.Property<string>("AddressStreet")
.HasMaxLength(255)
.HasColumnType("VARCHAR");
b.Property<string>("AddressZipCode")
.HasMaxLength(255)
.HasColumnType("VARCHAR");
b.Property<string>("BankAccountIBAN")
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<DateTime>("BirthDate")
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("EmailPrefix")
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<string>("FirstName")
.HasMaxLength(255)
.HasColumnType("VARCHAR");
b.Property<string>("Gender")
.HasMaxLength(255)
.HasColumnType("VARCHAR");
b.Property<string>("Hobbies")
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<string>("LastName")
.HasMaxLength(255)
.HasColumnType("VARCHAR");
b.Property<string>("NickName")
.HasMaxLength(255)
.HasColumnType("VARCHAR");
b.Property<string>("PhoneMobile")
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Aliases");
});
modelBuilder.Entity("AliasClientDb.Attachment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<byte[]>("Blob")
.IsRequired()
.HasColumnType("BLOB");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<Guid>("CredentialId")
.HasColumnType("TEXT");
b.Property<string>("Filename")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("CredentialId");
b.ToTable("Attachment");
});
modelBuilder.Entity("AliasClientDb.Credential", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<Guid>("AliasId")
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Notes")
.HasColumnType("TEXT");
b.Property<Guid>("ServiceId")
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.Property<string>("Username")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("AliasId");
b.HasIndex("ServiceId");
b.ToTable("Credentials");
});
modelBuilder.Entity("AliasClientDb.Password", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<Guid>("CredentialId")
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasMaxLength(255)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("CredentialId");
b.ToTable("Passwords");
});
modelBuilder.Entity("AliasClientDb.Service", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<byte[]>("Logo")
.HasColumnType("BLOB");
b.Property<string>("Name")
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.Property<string>("Url")
.HasMaxLength(255)
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Services");
});
modelBuilder.Entity("AliasClientDb.Attachment", b =>
{
b.HasOne("AliasClientDb.Credential", "Credential")
.WithMany("Attachments")
.HasForeignKey("CredentialId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Credential");
});
modelBuilder.Entity("AliasClientDb.Credential", b =>
{
b.HasOne("AliasClientDb.Alias", "Alias")
.WithMany("Credentials")
.HasForeignKey("AliasId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("AliasClientDb.Service", "Service")
.WithMany("Credentials")
.HasForeignKey("ServiceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Alias");
b.Navigation("Service");
});
modelBuilder.Entity("AliasClientDb.Password", b =>
{
b.HasOne("AliasClientDb.Credential", "Credential")
.WithMany("Passwords")
.HasForeignKey("CredentialId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Credential");
});
modelBuilder.Entity("AliasClientDb.Alias", b =>
{
b.Navigation("Credentials");
});
modelBuilder.Entity("AliasClientDb.Credential", b =>
{
b.Navigation("Attachments");
b.Navigation("Passwords");
});
modelBuilder.Entity("AliasClientDb.Service", b =>
{
b.Navigation("Credentials");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,170 @@
// <auto-generated/>
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace AliasClientDb.Migrations
{
/// <inheritdoc />
public partial class _100InitialMigration : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Aliases",
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
Gender = table.Column<string>(type: "VARCHAR", maxLength: 255, nullable: true),
FirstName = table.Column<string>(type: "VARCHAR", maxLength: 255, nullable: true),
LastName = table.Column<string>(type: "VARCHAR", maxLength: 255, nullable: true),
NickName = table.Column<string>(type: "VARCHAR", maxLength: 255, nullable: true),
BirthDate = table.Column<DateTime>(type: "TEXT", nullable: false),
AddressStreet = table.Column<string>(type: "VARCHAR", maxLength: 255, nullable: true),
AddressCity = table.Column<string>(type: "VARCHAR", maxLength: 255, nullable: true),
AddressState = table.Column<string>(type: "VARCHAR", maxLength: 255, nullable: true),
AddressZipCode = table.Column<string>(type: "VARCHAR", maxLength: 255, nullable: true),
AddressCountry = table.Column<string>(type: "VARCHAR", maxLength: 255, nullable: true),
Hobbies = table.Column<string>(type: "TEXT", maxLength: 255, nullable: true),
EmailPrefix = table.Column<string>(type: "TEXT", maxLength: 255, nullable: true),
PhoneMobile = table.Column<string>(type: "TEXT", maxLength: 255, nullable: true),
BankAccountIBAN = table.Column<string>(type: "TEXT", maxLength: 255, nullable: true),
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Aliases", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Services",
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
Name = table.Column<string>(type: "TEXT", maxLength: 255, nullable: true),
Url = table.Column<string>(type: "TEXT", maxLength: 255, nullable: true),
Logo = table.Column<byte[]>(type: "BLOB", nullable: true),
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Services", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Credentials",
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
AliasId = table.Column<Guid>(type: "TEXT", nullable: false),
Notes = table.Column<string>(type: "TEXT", nullable: true),
Username = table.Column<string>(type: "TEXT", nullable: false),
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
ServiceId = table.Column<Guid>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Credentials", x => x.Id);
table.ForeignKey(
name: "FK_Credentials_Aliases_AliasId",
column: x => x.AliasId,
principalTable: "Aliases",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_Credentials_Services_ServiceId",
column: x => x.ServiceId,
principalTable: "Services",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "Attachment",
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
Filename = table.Column<string>(type: "TEXT", maxLength: 255, nullable: false),
Blob = table.Column<byte[]>(type: "BLOB", nullable: false),
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
CredentialId = table.Column<Guid>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Attachment", x => x.Id);
table.ForeignKey(
name: "FK_Attachment_Credentials_CredentialId",
column: x => x.CredentialId,
principalTable: "Credentials",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "Passwords",
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
Value = table.Column<string>(type: "TEXT", maxLength: 255, nullable: true),
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
CredentialId = table.Column<Guid>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Passwords", x => x.Id);
table.ForeignKey(
name: "FK_Passwords_Credentials_CredentialId",
column: x => x.CredentialId,
principalTable: "Credentials",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Attachment_CredentialId",
table: "Attachment",
column: "CredentialId");
migrationBuilder.CreateIndex(
name: "IX_Credentials_AliasId",
table: "Credentials",
column: "AliasId");
migrationBuilder.CreateIndex(
name: "IX_Credentials_ServiceId",
table: "Credentials",
column: "ServiceId");
migrationBuilder.CreateIndex(
name: "IX_Passwords_CredentialId",
table: "Passwords",
column: "CredentialId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Attachment");
migrationBuilder.DropTable(
name: "Passwords");
migrationBuilder.DropTable(
name: "Credentials");
migrationBuilder.DropTable(
name: "Aliases");
migrationBuilder.DropTable(
name: "Services");
}
}
}

View File

@@ -0,0 +1,275 @@
// <auto-generated />
using System;
using AliasClientDb;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace AliasClientDb.Migrations
{
[DbContext(typeof(AliasClientDbContext))]
partial class AliasClientDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.6")
.HasAnnotation("Proxies:ChangeTracking", false)
.HasAnnotation("Proxies:CheckEquality", false)
.HasAnnotation("Proxies:LazyLoading", true);
modelBuilder.Entity("AliasClientDb.Alias", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<string>("AddressCity")
.HasMaxLength(255)
.HasColumnType("VARCHAR");
b.Property<string>("AddressCountry")
.HasMaxLength(255)
.HasColumnType("VARCHAR");
b.Property<string>("AddressState")
.HasMaxLength(255)
.HasColumnType("VARCHAR");
b.Property<string>("AddressStreet")
.HasMaxLength(255)
.HasColumnType("VARCHAR");
b.Property<string>("AddressZipCode")
.HasMaxLength(255)
.HasColumnType("VARCHAR");
b.Property<string>("BankAccountIBAN")
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<DateTime>("BirthDate")
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("EmailPrefix")
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<string>("FirstName")
.HasMaxLength(255)
.HasColumnType("VARCHAR");
b.Property<string>("Gender")
.HasMaxLength(255)
.HasColumnType("VARCHAR");
b.Property<string>("Hobbies")
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<string>("LastName")
.HasMaxLength(255)
.HasColumnType("VARCHAR");
b.Property<string>("NickName")
.HasMaxLength(255)
.HasColumnType("VARCHAR");
b.Property<string>("PhoneMobile")
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Aliases");
});
modelBuilder.Entity("AliasClientDb.Attachment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<byte[]>("Blob")
.IsRequired()
.HasColumnType("BLOB");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<Guid>("CredentialId")
.HasColumnType("TEXT");
b.Property<string>("Filename")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("CredentialId");
b.ToTable("Attachment");
});
modelBuilder.Entity("AliasClientDb.Credential", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<Guid>("AliasId")
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<string>("Notes")
.HasColumnType("TEXT");
b.Property<Guid>("ServiceId")
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.Property<string>("Username")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("AliasId");
b.HasIndex("ServiceId");
b.ToTable("Credentials");
});
modelBuilder.Entity("AliasClientDb.Password", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<Guid>("CredentialId")
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasMaxLength(255)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("CredentialId");
b.ToTable("Passwords");
});
modelBuilder.Entity("AliasClientDb.Service", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedAt")
.HasColumnType("TEXT");
b.Property<byte[]>("Logo")
.HasColumnType("BLOB");
b.Property<string>("Name")
.HasMaxLength(255)
.HasColumnType("TEXT");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("TEXT");
b.Property<string>("Url")
.HasMaxLength(255)
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Services");
});
modelBuilder.Entity("AliasClientDb.Attachment", b =>
{
b.HasOne("AliasClientDb.Credential", "Credential")
.WithMany("Attachments")
.HasForeignKey("CredentialId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Credential");
});
modelBuilder.Entity("AliasClientDb.Credential", b =>
{
b.HasOne("AliasClientDb.Alias", "Alias")
.WithMany("Credentials")
.HasForeignKey("AliasId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("AliasClientDb.Service", "Service")
.WithMany("Credentials")
.HasForeignKey("ServiceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Alias");
b.Navigation("Service");
});
modelBuilder.Entity("AliasClientDb.Password", b =>
{
b.HasOne("AliasClientDb.Credential", "Credential")
.WithMany("Passwords")
.HasForeignKey("CredentialId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Credential");
});
modelBuilder.Entity("AliasClientDb.Alias", b =>
{
b.Navigation("Credentials");
});
modelBuilder.Entity("AliasClientDb.Credential", b =>
{
b.Navigation("Attachments");
b.Navigation("Passwords");
});
modelBuilder.Entity("AliasClientDb.Service", b =>
{
b.Navigation("Credentials");
});
#pragma warning restore 612, 618
}
}
}