aboutsummaryrefslogtreecommitdiffstats
path: root/code/api/src
diff options
context:
space:
mode:
authorivarlovlie <git@ivarlovlie.no>2022-11-14 05:04:36 +0100
committerivarlovlie <git@ivarlovlie.no>2022-11-14 05:05:09 +0100
commit04a2b73805fc1213df42ef9af4cdcf27ff7c86db (patch)
tree818df6112e2b74ce3af6f2d9ce7535915967feed /code/api/src
parent94da43fd4e2c625babcf7cdabc5e82a47bcab2a5 (diff)
downloadgreatoffice-04a2b73805fc1213df42ef9af4cdcf27ff7c86db.tar.xz
greatoffice-04a2b73805fc1213df42ef9af4cdcf27ff7c86db.zip
refactor: Password reset service
- Use FulfillPasswordResetRequestResult to indicate fulfillment result - Rename db names forgot_password_requests > password_reset_requests
Diffstat (limited to 'code/api/src')
-rw-r--r--code/api/src/Data/Database/Internal/PasswordResetRequest.cs (renamed from code/api/src/Data/Database/Internal/ForgotPasswordRequest.cs)6
-rw-r--r--code/api/src/Data/Database/MainAppDatabase.cs4
-rw-r--r--code/api/src/Data/Enums/FulfillPasswordResetRequestResult.cs8
-rw-r--r--code/api/src/Data/Exceptions/ForgotPasswordRequestNotFoundException.cs21
-rw-r--r--code/api/src/Data/Exceptions/UserNotFoundException.cs19
-rw-r--r--code/api/src/Endpoints/Internal/Account/DeleteAccountRoute.cs4
-rw-r--r--code/api/src/Endpoints/Internal/PasswordResetRequests/FulfillResetRequestRoute.cs22
-rw-r--r--code/api/src/Migrations/20221114035223_RenameForgotPasswordRequests.Designer.cs1589
-rw-r--r--code/api/src/Migrations/20221114035223_RenameForgotPasswordRequests.cs78
-rw-r--r--code/api/src/Migrations/AppDbContextModelSnapshot.cs289
-rw-r--r--code/api/src/Program.cs14
-rw-r--r--code/api/src/Services/PasswordResetService.cs36
12 files changed, 1712 insertions, 378 deletions
diff --git a/code/api/src/Data/Database/Internal/ForgotPasswordRequest.cs b/code/api/src/Data/Database/Internal/PasswordResetRequest.cs
index 1510a35..ee73fd2 100644
--- a/code/api/src/Data/Database/Internal/ForgotPasswordRequest.cs
+++ b/code/api/src/Data/Database/Internal/PasswordResetRequest.cs
@@ -1,10 +1,10 @@
namespace IOL.GreatOffice.Api.Data.Database;
-public class ForgotPasswordRequest
+public class PasswordResetRequest
{
- public ForgotPasswordRequest() { }
+ public PasswordResetRequest() { }
- public ForgotPasswordRequest(User user) {
+ public PasswordResetRequest(User user) {
CreatedAt = AppDateTime.UtcNow;
Id = Guid.NewGuid();
User = user;
diff --git a/code/api/src/Data/Database/MainAppDatabase.cs b/code/api/src/Data/Database/MainAppDatabase.cs
index 41d62a7..fe3ed58 100644
--- a/code/api/src/Data/Database/MainAppDatabase.cs
+++ b/code/api/src/Data/Database/MainAppDatabase.cs
@@ -6,7 +6,7 @@ public class MainAppDatabase : DbContext, IDataProtectionKeyContext
{
public MainAppDatabase(DbContextOptions<MainAppDatabase> options) : base(options) { }
public DbSet<User> Users { get; set; }
- public DbSet<ForgotPasswordRequest> ForgotPasswordRequests { get; set; }
+ public DbSet<PasswordResetRequest> PasswordResetRequests { get; set; }
public DbSet<ApiAccessToken> AccessTokens { get; set; }
public DbSet<Tenant> Tenants { get; set; }
public DbSet<DataProtectionKey> DataProtectionKeys { get; set; }
@@ -27,7 +27,7 @@ public class MainAppDatabase : DbContext, IDataProtectionKeyContext
e.HasMany(n => n.Tenants);
e.ToTable("users");
});
- modelBuilder.Entity<ForgotPasswordRequest>(e => {
+ modelBuilder.Entity<PasswordResetRequest>(e => {
e.HasOne(c => c.User);
e.ToTable("forgot_password_requests");
});
diff --git a/code/api/src/Data/Enums/FulfillPasswordResetRequestResult.cs b/code/api/src/Data/Enums/FulfillPasswordResetRequestResult.cs
new file mode 100644
index 0000000..2a84c48
--- /dev/null
+++ b/code/api/src/Data/Enums/FulfillPasswordResetRequestResult.cs
@@ -0,0 +1,8 @@
+namespace IOL.GreatOffice.Api.Data.Enums;
+
+public enum FulfillPasswordResetRequestResult
+{
+ REQUEST_NOT_FOUND,
+ USER_NOT_FOUND,
+ FULFILLED
+} \ No newline at end of file
diff --git a/code/api/src/Data/Exceptions/ForgotPasswordRequestNotFoundException.cs b/code/api/src/Data/Exceptions/ForgotPasswordRequestNotFoundException.cs
deleted file mode 100644
index 02474b4..0000000
--- a/code/api/src/Data/Exceptions/ForgotPasswordRequestNotFoundException.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-namespace IOL.GreatOffice.Api.Data.Exceptions;
-
-[Serializable]
-public class ForgotPasswordRequestNotFoundException : Exception
-{
- //
- // For guidelines regarding the creation of new exception types, see
- // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconerrorraisinghandlingguidelines.asp
- // and
- // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp07192001.asp
- //
-
- public ForgotPasswordRequestNotFoundException() { }
- public ForgotPasswordRequestNotFoundException(string message) : base(message) { }
- public ForgotPasswordRequestNotFoundException(string message, Exception inner) : base(message, inner) { }
-
- protected ForgotPasswordRequestNotFoundException(
- SerializationInfo info,
- StreamingContext context
- ) : base(info, context) { }
-}
diff --git a/code/api/src/Data/Exceptions/UserNotFoundException.cs b/code/api/src/Data/Exceptions/UserNotFoundException.cs
deleted file mode 100644
index 06b57a9..0000000
--- a/code/api/src/Data/Exceptions/UserNotFoundException.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace IOL.GreatOffice.Api.Data.Exceptions;
-
-[Serializable]
-public class UserNotFoundException : Exception
-{
- // For guidelines regarding the creation of new exception types, see
- // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconerrorraisinghandlingguidelines.asp
- // and
- // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp07192001.asp
-
- public UserNotFoundException() { }
- public UserNotFoundException(string message) : base(message) { }
- public UserNotFoundException(string message, Exception inner) : base(message, inner) { }
-
- protected UserNotFoundException(
- SerializationInfo info,
- StreamingContext context
- ) : base(info, context) { }
-}
diff --git a/code/api/src/Endpoints/Internal/Account/DeleteAccountRoute.cs b/code/api/src/Endpoints/Internal/Account/DeleteAccountRoute.cs
index e5bbb10..f0e8362 100644
--- a/code/api/src/Endpoints/Internal/Account/DeleteAccountRoute.cs
+++ b/code/api/src/Endpoints/Internal/Account/DeleteAccountRoute.cs
@@ -28,9 +28,9 @@ public class DeleteAccountRoute : RouteBaseAsync.WithoutRequest.WithActionResult
return Ok();
}
- var passwordResets = _database.ForgotPasswordRequests.Where(c => c.UserId == user.Id);
+ var passwordResets = _database.PasswordResetRequests.Where(c => c.UserId == user.Id);
- _database.ForgotPasswordRequests.RemoveRange(passwordResets);
+ _database.PasswordResetRequests.RemoveRange(passwordResets);
_database.Users.Remove(user);
await _database.SaveChangesAsync(cancellationToken);
diff --git a/code/api/src/Endpoints/Internal/PasswordResetRequests/FulfillResetRequestRoute.cs b/code/api/src/Endpoints/Internal/PasswordResetRequests/FulfillResetRequestRoute.cs
index 6f71b2f..5749242 100644
--- a/code/api/src/Endpoints/Internal/PasswordResetRequests/FulfillResetRequestRoute.cs
+++ b/code/api/src/Endpoints/Internal/PasswordResetRequests/FulfillResetRequestRoute.cs
@@ -8,24 +8,14 @@ public class FulfillResetRequestRoute : RouteBaseAsync.WithRequest<FulfillResetR
_passwordResetService = passwordResetService;
}
- /// <summary>
- /// Fulfill a password reset request.
- /// </summary>
- /// <param name="request"></param>
- /// <param name="cancellationToken"></param>
- /// <returns></returns>
[AllowAnonymous]
[HttpPost("~/_/password-reset-request/fulfill")]
public override async Task<ActionResult> HandleAsync(FulfillResetRequestPayload request, CancellationToken cancellationToken = default) {
- try {
- var fulfilled = await _passwordResetService.FullFillRequestAsync(request.Id, request.NewPassword, cancellationToken);
- return Ok(fulfilled);
- } catch (Exception e) {
- if (e is ForgotPasswordRequestNotFoundException or UserNotFoundException) {
- return NotFound();
- }
-
- throw;
- }
+ return await _passwordResetService.FulfillRequestAsync(request.Id, request.NewPassword, cancellationToken) switch {
+ FulfillPasswordResetRequestResult.REQUEST_NOT_FOUND => NotFound(),
+ FulfillPasswordResetRequestResult.USER_NOT_FOUND => NotFound(),
+ FulfillPasswordResetRequestResult.FULFILLED => Ok(),
+ _ => StatusCode(500)
+ };
}
} \ No newline at end of file
diff --git a/code/api/src/Migrations/20221114035223_RenameForgotPasswordRequests.Designer.cs b/code/api/src/Migrations/20221114035223_RenameForgotPasswordRequests.Designer.cs
new file mode 100644
index 0000000..feb9805
--- /dev/null
+++ b/code/api/src/Migrations/20221114035223_RenameForgotPasswordRequests.Designer.cs
@@ -0,0 +1,1589 @@
+// <auto-generated />
+using System;
+using IOL.GreatOffice.Api.Data.Database;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+#nullable disable
+
+namespace IOL.GreatOffice.Api.Migrations
+{
+ [DbContext(typeof(MainAppDatabase))]
+ [Migration("20221114035223_RenameForgotPasswordRequests")]
+ partial class RenameForgotPasswordRequests
+ {
+ /// <inheritdoc />
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "7.0.0")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("CustomerCustomerGroup", b =>
+ {
+ b.Property<Guid>("CustomersId")
+ .HasColumnType("uuid")
+ .HasColumnName("customers_id");
+
+ b.Property<Guid>("GroupsId")
+ .HasColumnType("uuid")
+ .HasColumnName("groups_id");
+
+ b.HasKey("CustomersId", "GroupsId")
+ .HasName("pk_customer_customer_group");
+
+ b.HasIndex("GroupsId")
+ .HasDatabaseName("ix_customer_customer_group_groups_id");
+
+ b.ToTable("customer_customer_group", (string)null);
+ });
+
+ modelBuilder.Entity("CustomerProject", b =>
+ {
+ b.Property<Guid>("CustomersId")
+ .HasColumnType("uuid")
+ .HasColumnName("customers_id");
+
+ b.Property<Guid>("ProjectsId")
+ .HasColumnType("uuid")
+ .HasColumnName("projects_id");
+
+ b.HasKey("CustomersId", "ProjectsId")
+ .HasName("pk_customer_project");
+
+ b.HasIndex("ProjectsId")
+ .HasDatabaseName("ix_customer_project_projects_id");
+
+ b.ToTable("customer_project", (string)null);
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.ApiAccessToken", b =>
+ {
+ b.Property<Guid>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property<bool>("AllowCreate")
+ .HasColumnType("boolean")
+ .HasColumnName("allow_create");
+
+ b.Property<bool>("AllowDelete")
+ .HasColumnType("boolean")
+ .HasColumnName("allow_delete");
+
+ b.Property<bool>("AllowRead")
+ .HasColumnType("boolean")
+ .HasColumnName("allow_read");
+
+ b.Property<bool>("AllowUpdate")
+ .HasColumnType("boolean")
+ .HasColumnName("allow_update");
+
+ b.Property<DateTime>("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property<bool>("Deleted")
+ .HasColumnType("boolean")
+ .HasColumnName("deleted");
+
+ b.Property<DateTime?>("DeletedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("deleted_at");
+
+ b.Property<DateTime>("ExpiryDate")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("expiry_date");
+
+ b.Property<DateTime?>("ModifiedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("modified_at");
+
+ b.Property<Guid?>("UserId")
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.HasKey("Id")
+ .HasName("pk_api_access_tokens");
+
+ b.HasIndex("UserId")
+ .HasDatabaseName("ix_api_access_tokens_user_id");
+
+ b.ToTable("api_access_tokens", (string)null);
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.Customer", b =>
+ {
+ b.Property<Guid>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property<string>("Address1")
+ .HasColumnType("text")
+ .HasColumnName("address1");
+
+ b.Property<string>("Address2")
+ .HasColumnType("text")
+ .HasColumnName("address2");
+
+ b.Property<string>("Country")
+ .HasColumnType("text")
+ .HasColumnName("country");
+
+ b.Property<DateTime>("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property<Guid?>("CreatedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("created_by");
+
+ b.Property<string>("Currency")
+ .HasColumnType("text")
+ .HasColumnName("currency");
+
+ b.Property<string>("CustomerNumber")
+ .HasColumnType("text")
+ .HasColumnName("customer_number");
+
+ b.Property<string>("DefaultReference")
+ .HasColumnType("text")
+ .HasColumnName("default_reference");
+
+ b.Property<bool>("Deleted")
+ .HasColumnType("boolean")
+ .HasColumnName("deleted");
+
+ b.Property<DateTime?>("DeletedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("deleted_at");
+
+ b.Property<Guid?>("DeletedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("deleted_by");
+
+ b.Property<string>("Description")
+ .HasColumnType("text")
+ .HasColumnName("description");
+
+ b.Property<string>("Email")
+ .HasColumnType("text")
+ .HasColumnName("email");
+
+ b.Property<DateTime?>("ModifiedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("modified_at");
+
+ b.Property<Guid?>("ModifiedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("modified_by");
+
+ b.Property<string>("Name")
+ .HasColumnType("text")
+ .HasColumnName("name");
+
+ b.Property<string>("ORGNumber")
+ .HasColumnType("text")
+ .HasColumnName("org_number");
+
+ b.Property<Guid?>("OwnerId")
+ .HasColumnType("uuid")
+ .HasColumnName("owner_id");
+
+ b.Property<string>("Phone")
+ .HasColumnType("text")
+ .HasColumnName("phone");
+
+ b.Property<string>("PostalCity")
+ .HasColumnType("text")
+ .HasColumnName("postal_city");
+
+ b.Property<string>("PostalCode")
+ .HasColumnType("text")
+ .HasColumnName("postal_code");
+
+ b.Property<Guid?>("TenantId")
+ .HasColumnType("uuid")
+ .HasColumnName("tenant_id");
+
+ b.Property<Guid?>("UserId")
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.Property<string>("VATNumber")
+ .HasColumnType("text")
+ .HasColumnName("vat_number");
+
+ b.Property<string>("Website")
+ .HasColumnType("text")
+ .HasColumnName("website");
+
+ b.HasKey("Id")
+ .HasName("pk_customers");
+
+ b.HasIndex("OwnerId")
+ .HasDatabaseName("ix_customers_owner_id");
+
+ b.HasIndex("TenantId")
+ .HasDatabaseName("ix_customers_tenant_id");
+
+ b.HasIndex("UserId")
+ .HasDatabaseName("ix_customers_user_id");
+
+ b.ToTable("customers", (string)null);
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.CustomerContact", b =>
+ {
+ b.Property<Guid>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property<DateTime>("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property<Guid?>("CreatedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("created_by");
+
+ b.Property<Guid?>("CustomerId")
+ .HasColumnType("uuid")
+ .HasColumnName("customer_id");
+
+ b.Property<bool>("Deleted")
+ .HasColumnType("boolean")
+ .HasColumnName("deleted");
+
+ b.Property<DateTime?>("DeletedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("deleted_at");
+
+ b.Property<Guid?>("DeletedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("deleted_by");
+
+ b.Property<string>("Email")
+ .HasColumnType("text")
+ .HasColumnName("email");
+
+ b.Property<string>("FirstName")
+ .HasColumnType("text")
+ .HasColumnName("first_name");
+
+ b.Property<string>("LastName")
+ .HasColumnType("text")
+ .HasColumnName("last_name");
+
+ b.Property<DateTime?>("ModifiedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("modified_at");
+
+ b.Property<Guid?>("ModifiedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("modified_by");
+
+ b.Property<string>("Note")
+ .HasColumnType("text")
+ .HasColumnName("note");
+
+ b.Property<string>("Phone")
+ .HasColumnType("text")
+ .HasColumnName("phone");
+
+ b.Property<Guid?>("TenantId")
+ .HasColumnType("uuid")
+ .HasColumnName("tenant_id");
+
+ b.Property<Guid?>("UserId")
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.Property<string>("WorkTitle")
+ .HasColumnType("text")
+ .HasColumnName("work_title");
+
+ b.HasKey("Id")
+ .HasName("pk_customer_contacts");
+
+ b.HasIndex("CustomerId")
+ .HasDatabaseName("ix_customer_contacts_customer_id");
+
+ b.HasIndex("TenantId")
+ .HasDatabaseName("ix_customer_contacts_tenant_id");
+
+ b.HasIndex("UserId")
+ .HasDatabaseName("ix_customer_contacts_user_id");
+
+ b.ToTable("customer_contacts", (string)null);
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.CustomerEvent", b =>
+ {
+ b.Property<Guid>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property<DateTime>("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property<Guid?>("CreatedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("created_by");
+
+ b.Property<Guid?>("CustomerId")
+ .HasColumnType("uuid")
+ .HasColumnName("customer_id");
+
+ b.Property<bool>("Deleted")
+ .HasColumnType("boolean")
+ .HasColumnName("deleted");
+
+ b.Property<DateTime?>("DeletedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("deleted_at");
+
+ b.Property<Guid?>("DeletedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("deleted_by");
+
+ b.Property<DateTime?>("ModifiedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("modified_at");
+
+ b.Property<Guid?>("ModifiedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("modified_by");
+
+ b.Property<string>("Note")
+ .HasColumnType("text")
+ .HasColumnName("note");
+
+ b.Property<Guid?>("TenantId")
+ .HasColumnType("uuid")
+ .HasColumnName("tenant_id");
+
+ b.Property<string>("Title")
+ .HasColumnType("text")
+ .HasColumnName("title");
+
+ b.Property<Guid?>("UserId")
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.HasKey("Id")
+ .HasName("pk_customer_events");
+
+ b.HasIndex("CustomerId")
+ .HasDatabaseName("ix_customer_events_customer_id");
+
+ b.HasIndex("TenantId")
+ .HasDatabaseName("ix_customer_events_tenant_id");
+
+ b.HasIndex("UserId")
+ .HasDatabaseName("ix_customer_events_user_id");
+
+ b.ToTable("customer_events", (string)null);
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.CustomerGroup", b =>
+ {
+ b.Property<Guid>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property<DateTime>("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property<Guid?>("CreatedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("created_by");
+
+ b.Property<bool>("Deleted")
+ .HasColumnType("boolean")
+ .HasColumnName("deleted");
+
+ b.Property<DateTime?>("DeletedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("deleted_at");
+
+ b.Property<Guid?>("DeletedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("deleted_by");
+
+ b.Property<DateTime?>("ModifiedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("modified_at");
+
+ b.Property<Guid?>("ModifiedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("modified_by");
+
+ b.Property<string>("Name")
+ .HasColumnType("text")
+ .HasColumnName("name");
+
+ b.Property<Guid?>("TenantId")
+ .HasColumnType("uuid")
+ .HasColumnName("tenant_id");
+
+ b.Property<Guid?>("UserId")
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.HasKey("Id")
+ .HasName("pk_customer_groups");
+
+ b.HasIndex("TenantId")
+ .HasDatabaseName("ix_customer_groups_tenant_id");
+
+ b.HasIndex("UserId")
+ .HasDatabaseName("ix_customer_groups_user_id");
+
+ b.ToTable("customer_groups", (string)null);
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.PasswordResetRequest", b =>
+ {
+ b.Property<Guid>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property<DateTime>("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property<Guid>("UserId")
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.HasKey("Id")
+ .HasName("pk_password_reset_requests");
+
+ b.HasIndex("UserId")
+ .HasDatabaseName("ix_password_reset_requests_user_id");
+
+ b.ToTable("password_reset_requests", (string)null);
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.Project", b =>
+ {
+ b.Property<Guid>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property<DateTime>("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property<Guid?>("CreatedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("created_by");
+
+ b.Property<bool>("Deleted")
+ .HasColumnType("boolean")
+ .HasColumnName("deleted");
+
+ b.Property<DateTime?>("DeletedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("deleted_at");
+
+ b.Property<Guid?>("DeletedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("deleted_by");
+
+ b.Property<string>("Description")
+ .HasColumnType("text")
+ .HasColumnName("description");
+
+ b.Property<DateTime?>("ModifiedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("modified_at");
+
+ b.Property<Guid?>("ModifiedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("modified_by");
+
+ b.Property<string>("Name")
+ .HasColumnType("text")
+ .HasColumnName("name");
+
+ b.Property<DateTime?>("Start")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("start");
+
+ b.Property<DateTime?>("Stop")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("stop");
+
+ b.Property<Guid?>("TenantId")
+ .HasColumnType("uuid")
+ .HasColumnName("tenant_id");
+
+ b.Property<Guid?>("UserId")
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.HasKey("Id")
+ .HasName("pk_projects");
+
+ b.HasIndex("TenantId")
+ .HasDatabaseName("ix_projects_tenant_id");
+
+ b.HasIndex("UserId")
+ .HasDatabaseName("ix_projects_user_id");
+
+ b.ToTable("projects", (string)null);
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.ProjectLabel", b =>
+ {
+ b.Property<Guid>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property<string>("Color")
+ .HasColumnType("text")
+ .HasColumnName("color");
+
+ b.Property<DateTime>("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property<Guid?>("CreatedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("created_by");
+
+ b.Property<bool>("Deleted")
+ .HasColumnType("boolean")
+ .HasColumnName("deleted");
+
+ b.Property<DateTime?>("DeletedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("deleted_at");
+
+ b.Property<Guid?>("DeletedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("deleted_by");
+
+ b.Property<DateTime?>("ModifiedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("modified_at");
+
+ b.Property<Guid?>("ModifiedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("modified_by");
+
+ b.Property<Guid?>("ProjectId")
+ .HasColumnType("uuid")
+ .HasColumnName("project_id");
+
+ b.Property<Guid?>("TenantId")
+ .HasColumnType("uuid")
+ .HasColumnName("tenant_id");
+
+ b.Property<Guid?>("UserId")
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.Property<string>("Value")
+ .HasColumnType("text")
+ .HasColumnName("value");
+
+ b.HasKey("Id")
+ .HasName("pk_project_labels");
+
+ b.HasIndex("ProjectId")
+ .HasDatabaseName("ix_project_labels_project_id");
+
+ b.HasIndex("TenantId")
+ .HasDatabaseName("ix_project_labels_tenant_id");
+
+ b.HasIndex("UserId")
+ .HasDatabaseName("ix_project_labels_user_id");
+
+ b.ToTable("project_labels", (string)null);
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.ProjectMember", b =>
+ {
+ b.Property<Guid>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property<DateTime>("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property<bool>("Deleted")
+ .HasColumnType("boolean")
+ .HasColumnName("deleted");
+
+ b.Property<DateTime?>("DeletedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("deleted_at");
+
+ b.Property<DateTime?>("ModifiedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("modified_at");
+
+ b.Property<Guid?>("ProjectId")
+ .HasColumnType("uuid")
+ .HasColumnName("project_id");
+
+ b.Property<int>("Role")
+ .HasColumnType("integer")
+ .HasColumnName("role");
+
+ b.Property<Guid?>("UserId")
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.HasKey("Id")
+ .HasName("pk_project_members");
+
+ b.HasIndex("ProjectId")
+ .HasDatabaseName("ix_project_members_project_id");
+
+ b.HasIndex("UserId")
+ .HasDatabaseName("ix_project_members_user_id");
+
+ b.ToTable("project_members", (string)null);
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.Tenant", b =>
+ {
+ b.Property<Guid>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property<string>("ContactEmail")
+ .HasColumnType("text")
+ .HasColumnName("contact_email");
+
+ b.Property<DateTime>("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property<Guid?>("CreatedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("created_by");
+
+ b.Property<bool>("Deleted")
+ .HasColumnType("boolean")
+ .HasColumnName("deleted");
+
+ b.Property<DateTime?>("DeletedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("deleted_at");
+
+ b.Property<Guid?>("DeletedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("deleted_by");
+
+ b.Property<string>("Description")
+ .HasColumnType("text")
+ .HasColumnName("description");
+
+ b.Property<Guid>("MasterUserId")
+ .HasColumnType("uuid")
+ .HasColumnName("master_user_id");
+
+ b.Property<string>("MasterUserPassword")
+ .HasColumnType("text")
+ .HasColumnName("master_user_password");
+
+ b.Property<DateTime?>("ModifiedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("modified_at");
+
+ b.Property<Guid?>("ModifiedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("modified_by");
+
+ b.Property<string>("Name")
+ .HasColumnType("text")
+ .HasColumnName("name");
+
+ b.Property<Guid?>("OwningTenantId")
+ .HasColumnType("uuid")
+ .HasColumnName("owning_tenant_id");
+
+ b.Property<Guid?>("TenantId")
+ .HasColumnType("uuid")
+ .HasColumnName("tenant_id");
+
+ b.Property<Guid?>("UserId")
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.HasKey("Id")
+ .HasName("pk_tenants");
+
+ b.HasIndex("OwningTenantId")
+ .HasDatabaseName("ix_tenants_owning_tenant_id");
+
+ b.HasIndex("UserId")
+ .HasDatabaseName("ix_tenants_user_id");
+
+ b.ToTable("tenants", (string)null);
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.Todo", b =>
+ {
+ b.Property<Guid>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property<Guid?>("AssignedToId")
+ .HasColumnType("uuid")
+ .HasColumnName("assigned_to_id");
+
+ b.Property<DateTime?>("ClosedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("closed_at");
+
+ b.Property<Guid?>("ClosedById")
+ .HasColumnType("uuid")
+ .HasColumnName("closed_by_id");
+
+ b.Property<Guid>("CollectionId")
+ .HasColumnType("uuid")
+ .HasColumnName("collection_id");
+
+ b.Property<DateTime>("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property<Guid?>("CreatedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("created_by");
+
+ b.Property<bool>("Deleted")
+ .HasColumnType("boolean")
+ .HasColumnName("deleted");
+
+ b.Property<DateTime?>("DeletedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("deleted_at");
+
+ b.Property<Guid?>("DeletedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("deleted_by");
+
+ b.Property<string>("Description")
+ .HasColumnType("text")
+ .HasColumnName("description");
+
+ b.Property<DateTime?>("ModifiedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("modified_at");
+
+ b.Property<Guid?>("ModifiedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("modified_by");
+
+ b.Property<string>("PublicId")
+ .HasColumnType("text")
+ .HasColumnName("public_id");
+
+ b.Property<Guid?>("TenantId")
+ .HasColumnType("uuid")
+ .HasColumnName("tenant_id");
+
+ b.Property<string>("Title")
+ .HasColumnType("text")
+ .HasColumnName("title");
+
+ b.Property<Guid?>("UserId")
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.HasKey("Id")
+ .HasName("pk_todos");
+
+ b.HasIndex("AssignedToId")
+ .HasDatabaseName("ix_todos_assigned_to_id");
+
+ b.HasIndex("ClosedById")
+ .HasDatabaseName("ix_todos_closed_by_id");
+
+ b.HasIndex("CollectionId")
+ .HasDatabaseName("ix_todos_collection_id");
+
+ b.HasIndex("TenantId")
+ .HasDatabaseName("ix_todos_tenant_id");
+
+ b.HasIndex("UserId")
+ .HasDatabaseName("ix_todos_user_id");
+
+ b.ToTable("todos", (string)null);
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.TodoCollection", b =>
+ {
+ b.Property<Guid>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property<DateTime>("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property<Guid?>("CreatedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("created_by");
+
+ b.Property<bool>("Deleted")
+ .HasColumnType("boolean")
+ .HasColumnName("deleted");
+
+ b.Property<DateTime?>("DeletedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("deleted_at");
+
+ b.Property<Guid?>("DeletedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("deleted_by");
+
+ b.Property<string>("Description")
+ .HasColumnType("text")
+ .HasColumnName("description");
+
+ b.Property<DateTime?>("ModifiedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("modified_at");
+
+ b.Property<Guid?>("ModifiedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("modified_by");
+
+ b.Property<string>("Name")
+ .HasColumnType("text")
+ .HasColumnName("name");
+
+ b.Property<Guid?>("ProjectId")
+ .HasColumnType("uuid")
+ .HasColumnName("project_id");
+
+ b.Property<Guid?>("TenantId")
+ .HasColumnType("uuid")
+ .HasColumnName("tenant_id");
+
+ b.Property<Guid?>("UserId")
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.Property<int>("Visibility")
+ .HasColumnType("integer")
+ .HasColumnName("visibility");
+
+ b.HasKey("Id")
+ .HasName("pk_todo_collections");
+
+ b.HasIndex("ProjectId")
+ .HasDatabaseName("ix_todo_collections_project_id");
+
+ b.HasIndex("TenantId")
+ .HasDatabaseName("ix_todo_collections_tenant_id");
+
+ b.HasIndex("UserId")
+ .HasDatabaseName("ix_todo_collections_user_id");
+
+ b.ToTable("todo_collections", (string)null);
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.TodoCollectionAccessControl", b =>
+ {
+ b.Property<Guid>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property<bool>("CanBrowse")
+ .HasColumnType("boolean")
+ .HasColumnName("can_browse");
+
+ b.Property<bool>("CanComment")
+ .HasColumnType("boolean")
+ .HasColumnName("can_comment");
+
+ b.Property<bool>("CanEdit")
+ .HasColumnType("boolean")
+ .HasColumnName("can_edit");
+
+ b.Property<bool>("CanSubmit")
+ .HasColumnType("boolean")
+ .HasColumnName("can_submit");
+
+ b.Property<Guid?>("CollectionId")
+ .HasColumnType("uuid")
+ .HasColumnName("collection_id");
+
+ b.Property<DateTime>("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property<bool>("Deleted")
+ .HasColumnType("boolean")
+ .HasColumnName("deleted");
+
+ b.Property<DateTime?>("DeletedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("deleted_at");
+
+ b.Property<DateTime?>("ModifiedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("modified_at");
+
+ b.Property<Guid?>("UserId")
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.HasKey("Id")
+ .HasName("pk_todo_collection_access_controls");
+
+ b.HasIndex("CollectionId")
+ .HasDatabaseName("ix_todo_collection_access_controls_collection_id");
+
+ b.HasIndex("UserId")
+ .HasDatabaseName("ix_todo_collection_access_controls_user_id");
+
+ b.ToTable("todo_collection_access_controls", (string)null);
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.TodoComment", b =>
+ {
+ b.Property<Guid>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property<int?>("ClosingStatement")
+ .HasColumnType("integer")
+ .HasColumnName("closing_statement");
+
+ b.Property<DateTime>("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property<Guid?>("CreatedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("created_by");
+
+ b.Property<bool>("Deleted")
+ .HasColumnType("boolean")
+ .HasColumnName("deleted");
+
+ b.Property<DateTime?>("DeletedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("deleted_at");
+
+ b.Property<Guid?>("DeletedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("deleted_by");
+
+ b.Property<DateTime?>("ModifiedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("modified_at");
+
+ b.Property<Guid?>("ModifiedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("modified_by");
+
+ b.Property<Guid?>("TenantId")
+ .HasColumnType("uuid")
+ .HasColumnName("tenant_id");
+
+ b.Property<Guid?>("TodoId")
+ .HasColumnType("uuid")
+ .HasColumnName("todo_id");
+
+ b.Property<Guid?>("UserId")
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.Property<string>("Value")
+ .HasColumnType("text")
+ .HasColumnName("value");
+
+ b.HasKey("Id")
+ .HasName("pk_todo_comments");
+
+ b.HasIndex("TenantId")
+ .HasDatabaseName("ix_todo_comments_tenant_id");
+
+ b.HasIndex("TodoId")
+ .HasDatabaseName("ix_todo_comments_todo_id");
+
+ b.HasIndex("UserId")
+ .HasDatabaseName("ix_todo_comments_user_id");
+
+ b.ToTable("todo_comments", (string)null);
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.TodoLabel", b =>
+ {
+ b.Property<Guid>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property<string>("Color")
+ .HasColumnType("text")
+ .HasColumnName("color");
+
+ b.Property<DateTime>("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property<Guid?>("CreatedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("created_by");
+
+ b.Property<bool>("Deleted")
+ .HasColumnType("boolean")
+ .HasColumnName("deleted");
+
+ b.Property<DateTime?>("DeletedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("deleted_at");
+
+ b.Property<Guid?>("DeletedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("deleted_by");
+
+ b.Property<DateTime?>("ModifiedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("modified_at");
+
+ b.Property<Guid?>("ModifiedBy")
+ .HasColumnType("uuid")
+ .HasColumnName("modified_by");
+
+ b.Property<string>("Name")
+ .HasColumnType("text")
+ .HasColumnName("name");
+
+ b.Property<Guid?>("TenantId")
+ .HasColumnType("uuid")
+ .HasColumnName("tenant_id");
+
+ b.Property<Guid?>("TodoId")
+ .HasColumnType("uuid")
+ .HasColumnName("todo_id");
+
+ b.Property<Guid?>("UserId")
+ .HasColumnType("uuid")
+ .HasColumnName("user_id");
+
+ b.HasKey("Id")
+ .HasName("pk_todo_labels");
+
+ b.HasIndex("TenantId")
+ .HasDatabaseName("ix_todo_labels_tenant_id");
+
+ b.HasIndex("TodoId")
+ .HasDatabaseName("ix_todo_labels_todo_id");
+
+ b.HasIndex("UserId")
+ .HasDatabaseName("ix_todo_labels_user_id");
+
+ b.ToTable("todo_labels", (string)null);
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.User", b =>
+ {
+ b.Property<Guid>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("uuid")
+ .HasColumnName("id");
+
+ b.Property<DateTime>("CreatedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("created_at");
+
+ b.Property<bool>("Deleted")
+ .HasColumnType("boolean")
+ .HasColumnName("deleted");
+
+ b.Property<DateTime?>("DeletedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("deleted_at");
+
+ b.Property<string>("Email")
+ .HasColumnType("text")
+ .HasColumnName("email");
+
+ b.Property<string>("FirstName")
+ .HasColumnType("text")
+ .HasColumnName("first_name");
+
+ b.Property<string>("LastName")
+ .HasColumnType("text")
+ .HasColumnName("last_name");
+
+ b.Property<DateTime?>("ModifiedAt")
+ .HasColumnType("timestamp with time zone")
+ .HasColumnName("modified_at");
+
+ b.Property<string>("Password")
+ .HasColumnType("text")
+ .HasColumnName("password");
+
+ b.Property<Guid?>("TenantId")
+ .HasColumnType("uuid")
+ .HasColumnName("tenant_id");
+
+ b.Property<string>("Username")
+ .HasColumnType("text")
+ .HasColumnName("username");
+
+ b.HasKey("Id")
+ .HasName("pk_users");
+
+ b.HasIndex("TenantId")
+ .HasDatabaseName("ix_users_tenant_id");
+
+ b.ToTable("users", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b =>
+ {
+ b.Property<int>("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
+
+ b.Property<string>("FriendlyName")
+ .HasColumnType("text")
+ .HasColumnName("friendly_name");
+
+ b.Property<string>("Xml")
+ .HasColumnType("text")
+ .HasColumnName("xml");
+
+ b.HasKey("Id")
+ .HasName("pk_data_protection_keys");
+
+ b.ToTable("data_protection_keys", (string)null);
+ });
+
+ modelBuilder.Entity("CustomerCustomerGroup", b =>
+ {
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.Customer", null)
+ .WithMany()
+ .HasForeignKey("CustomersId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_customer_customer_group_customers_customers_id");
+
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.CustomerGroup", null)
+ .WithMany()
+ .HasForeignKey("GroupsId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_customer_customer_group_customer_groups_groups_id");
+ });
+
+ modelBuilder.Entity("CustomerProject", b =>
+ {
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.Customer", null)
+ .WithMany()
+ .HasForeignKey("CustomersId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_customer_project_customers_customers_id");
+
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.Project", null)
+ .WithMany()
+ .HasForeignKey("ProjectsId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_customer_project_projects_projects_id");
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.ApiAccessToken", b =>
+ {
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.User", "User")
+ .WithMany()
+ .HasForeignKey("UserId")
+ .HasConstraintName("fk_api_access_tokens_users_user_id");
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.Customer", b =>
+ {
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.User", "Owner")
+ .WithMany()
+ .HasForeignKey("OwnerId")
+ .HasConstraintName("fk_customers_users_owner_id");
+
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.Tenant", "OwningTenant")
+ .WithMany()
+ .HasForeignKey("TenantId")
+ .HasConstraintName("fk_customers_tenants_tenant_id");
+
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.User", "OwningUser")
+ .WithMany()
+ .HasForeignKey("UserId")
+ .HasConstraintName("fk_customers_users_user_id");
+
+ b.Navigation("Owner");
+
+ b.Navigation("OwningTenant");
+
+ b.Navigation("OwningUser");
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.CustomerContact", b =>
+ {
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.Customer", "Customer")
+ .WithMany("Contacts")
+ .HasForeignKey("CustomerId")
+ .HasConstraintName("fk_customer_contacts_customers_customer_id");
+
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.Tenant", "OwningTenant")
+ .WithMany()
+ .HasForeignKey("TenantId")
+ .HasConstraintName("fk_customer_contacts_tenants_tenant_id");
+
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.User", "OwningUser")
+ .WithMany()
+ .HasForeignKey("UserId")
+ .HasConstraintName("fk_customer_contacts_users_user_id");
+
+ b.Navigation("Customer");
+
+ b.Navigation("OwningTenant");
+
+ b.Navigation("OwningUser");
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.CustomerEvent", b =>
+ {
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.Customer", "Customer")
+ .WithMany("Events")
+ .HasForeignKey("CustomerId")
+ .HasConstraintName("fk_customer_events_customers_customer_id");
+
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.Tenant", "OwningTenant")
+ .WithMany()
+ .HasForeignKey("TenantId")
+ .HasConstraintName("fk_customer_events_tenants_tenant_id");
+
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.User", "OwningUser")
+ .WithMany()
+ .HasForeignKey("UserId")
+ .HasConstraintName("fk_customer_events_users_user_id");
+
+ b.Navigation("Customer");
+
+ b.Navigation("OwningTenant");
+
+ b.Navigation("OwningUser");
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.CustomerGroup", b =>
+ {
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.Tenant", "OwningTenant")
+ .WithMany()
+ .HasForeignKey("TenantId")
+ .HasConstraintName("fk_customer_groups_tenants_tenant_id");
+
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.User", "OwningUser")
+ .WithMany()
+ .HasForeignKey("UserId")
+ .HasConstraintName("fk_customer_groups_users_user_id");
+
+ b.Navigation("OwningTenant");
+
+ b.Navigation("OwningUser");
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.PasswordResetRequest", b =>
+ {
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.User", "User")
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_password_reset_requests_users_user_id");
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.Project", b =>
+ {
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.Tenant", "OwningTenant")
+ .WithMany()
+ .HasForeignKey("TenantId")
+ .HasConstraintName("fk_projects_tenants_tenant_id");
+
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.User", "OwningUser")
+ .WithMany()
+ .HasForeignKey("UserId")
+ .HasConstraintName("fk_projects_users_user_id");
+
+ b.Navigation("OwningTenant");
+
+ b.Navigation("OwningUser");
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.ProjectLabel", b =>
+ {
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.Project", "Project")
+ .WithMany("Labels")
+ .HasForeignKey("ProjectId")
+ .HasConstraintName("fk_project_labels_projects_project_id");
+
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.Tenant", "OwningTenant")
+ .WithMany()
+ .HasForeignKey("TenantId")
+ .HasConstraintName("fk_project_labels_tenants_tenant_id");
+
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.User", "OwningUser")
+ .WithMany()
+ .HasForeignKey("UserId")
+ .HasConstraintName("fk_project_labels_users_user_id");
+
+ b.Navigation("OwningTenant");
+
+ b.Navigation("OwningUser");
+
+ b.Navigation("Project");
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.ProjectMember", b =>
+ {
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.Project", "Project")
+ .WithMany("Members")
+ .HasForeignKey("ProjectId")
+ .HasConstraintName("fk_project_members_projects_project_id");
+
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.User", "User")
+ .WithMany()
+ .HasForeignKey("UserId")
+ .HasConstraintName("fk_project_members_users_user_id");
+
+ b.Navigation("Project");
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.Tenant", b =>
+ {
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.Tenant", "OwningTenant")
+ .WithMany()
+ .HasForeignKey("OwningTenantId")
+ .HasConstraintName("fk_tenants_tenants_owning_tenant_id");
+
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.User", "OwningUser")
+ .WithMany("Tenants")
+ .HasForeignKey("UserId")
+ .HasConstraintName("fk_tenants_users_user_id");
+
+ b.Navigation("OwningTenant");
+
+ b.Navigation("OwningUser");
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.Todo", b =>
+ {
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.User", "AssignedTo")
+ .WithMany()
+ .HasForeignKey("AssignedToId")
+ .HasConstraintName("fk_todos_users_assigned_to_id");
+
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.User", "ClosedBy")
+ .WithMany()
+ .HasForeignKey("ClosedById")
+ .HasConstraintName("fk_todos_users_closed_by_id");
+
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.TodoCollection", "Collection")
+ .WithMany()
+ .HasForeignKey("CollectionId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired()
+ .HasConstraintName("fk_todos_todo_projects_collection_id");
+
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.Tenant", "OwningTenant")
+ .WithMany()
+ .HasForeignKey("TenantId")
+ .HasConstraintName("fk_todos_tenants_tenant_id");
+
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.User", "OwningUser")
+ .WithMany()
+ .HasForeignKey("UserId")
+ .HasConstraintName("fk_todos_users_user_id");
+
+ b.Navigation("AssignedTo");
+
+ b.Navigation("ClosedBy");
+
+ b.Navigation("Collection");
+
+ b.Navigation("OwningTenant");
+
+ b.Navigation("OwningUser");
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.TodoCollection", b =>
+ {
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.Project", "Project")
+ .WithMany()
+ .HasForeignKey("ProjectId")
+ .HasConstraintName("fk_todo_collections_projects_project_id");
+
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.Tenant", "OwningTenant")
+ .WithMany()
+ .HasForeignKey("TenantId")
+ .HasConstraintName("fk_todo_collections_tenants_tenant_id");
+
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.User", "OwningUser")
+ .WithMany()
+ .HasForeignKey("UserId")
+ .HasConstraintName("fk_todo_collections_users_user_id");
+
+ b.Navigation("OwningTenant");
+
+ b.Navigation("OwningUser");
+
+ b.Navigation("Project");
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.TodoCollectionAccessControl", b =>
+ {
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.TodoCollection", "Collection")
+ .WithMany("AccessControls")
+ .HasForeignKey("CollectionId")
+ .HasConstraintName("fk_todo_collection_access_controls_todo_collections_collection");
+
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.User", "User")
+ .WithMany()
+ .HasForeignKey("UserId")
+ .HasConstraintName("fk_todo_collection_access_controls_users_user_id");
+
+ b.Navigation("Collection");
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.TodoComment", b =>
+ {
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.Tenant", "OwningTenant")
+ .WithMany()
+ .HasForeignKey("TenantId")
+ .HasConstraintName("fk_todo_comments_tenants_tenant_id");
+
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.Todo", "Todo")
+ .WithMany("Comments")
+ .HasForeignKey("TodoId")
+ .HasConstraintName("fk_todo_comments_todos_todo_id");
+
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.User", "OwningUser")
+ .WithMany()
+ .HasForeignKey("UserId")
+ .HasConstraintName("fk_todo_comments_users_user_id");
+
+ b.Navigation("OwningTenant");
+
+ b.Navigation("OwningUser");
+
+ b.Navigation("Todo");
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.TodoLabel", b =>
+ {
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.Tenant", "OwningTenant")
+ .WithMany()
+ .HasForeignKey("TenantId")
+ .HasConstraintName("fk_todo_labels_tenants_tenant_id");
+
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.Todo", "Todo")
+ .WithMany("Labels")
+ .HasForeignKey("TodoId")
+ .HasConstraintName("fk_todo_labels_todos_todo_id");
+
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.User", "OwningUser")
+ .WithMany()
+ .HasForeignKey("UserId")
+ .HasConstraintName("fk_todo_labels_users_user_id");
+
+ b.Navigation("OwningTenant");
+
+ b.Navigation("OwningUser");
+
+ b.Navigation("Todo");
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.User", b =>
+ {
+ b.HasOne("IOL.GreatOffice.Api.Data.Database.Tenant", null)
+ .WithMany("Users")
+ .HasForeignKey("TenantId")
+ .HasConstraintName("fk_users_tenants_tenant_id");
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.Customer", b =>
+ {
+ b.Navigation("Contacts");
+
+ b.Navigation("Events");
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.Project", b =>
+ {
+ b.Navigation("Labels");
+
+ b.Navigation("Members");
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.Tenant", b =>
+ {
+ b.Navigation("Users");
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.Todo", b =>
+ {
+ b.Navigation("Comments");
+
+ b.Navigation("Labels");
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.TodoCollection", b =>
+ {
+ b.Navigation("AccessControls");
+ });
+
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.User", b =>
+ {
+ b.Navigation("Tenants");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/code/api/src/Migrations/20221114035223_RenameForgotPasswordRequests.cs b/code/api/src/Migrations/20221114035223_RenameForgotPasswordRequests.cs
new file mode 100644
index 0000000..8b256e7
--- /dev/null
+++ b/code/api/src/Migrations/20221114035223_RenameForgotPasswordRequests.cs
@@ -0,0 +1,78 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace IOL.GreatOffice.Api.Migrations
+{
+ /// <inheritdoc />
+ public partial class RenameForgotPasswordRequests : Migration
+ {
+ /// <inheritdoc />
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropForeignKey(
+ name: "fk_forgot_password_requests_users_user_id",
+ table: "forgot_password_requests");
+
+ migrationBuilder.DropPrimaryKey(
+ name: "pk_forgot_password_requests",
+ table: "forgot_password_requests");
+
+ migrationBuilder.RenameTable(
+ name: "forgot_password_requests",
+ newName: "password_reset_requests");
+
+ migrationBuilder.RenameIndex(
+ name: "ix_forgot_password_requests_user_id",
+ table: "password_reset_requests",
+ newName: "ix_password_reset_requests_user_id");
+
+ migrationBuilder.AddPrimaryKey(
+ name: "pk_password_reset_requests",
+ table: "password_reset_requests",
+ column: "id");
+
+ migrationBuilder.AddForeignKey(
+ name: "fk_password_reset_requests_users_user_id",
+ table: "password_reset_requests",
+ column: "user_id",
+ principalTable: "users",
+ principalColumn: "id",
+ onDelete: ReferentialAction.Cascade);
+ }
+
+ /// <inheritdoc />
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropForeignKey(
+ name: "fk_password_reset_requests_users_user_id",
+ table: "password_reset_requests");
+
+ migrationBuilder.DropPrimaryKey(
+ name: "pk_password_reset_requests",
+ table: "password_reset_requests");
+
+ migrationBuilder.RenameTable(
+ name: "password_reset_requests",
+ newName: "forgot_password_requests");
+
+ migrationBuilder.RenameIndex(
+ name: "ix_password_reset_requests_user_id",
+ table: "forgot_password_requests",
+ newName: "ix_forgot_password_requests_user_id");
+
+ migrationBuilder.AddPrimaryKey(
+ name: "pk_forgot_password_requests",
+ table: "forgot_password_requests",
+ column: "id");
+
+ migrationBuilder.AddForeignKey(
+ name: "fk_forgot_password_requests_users_user_id",
+ table: "forgot_password_requests",
+ column: "user_id",
+ principalTable: "users",
+ principalColumn: "id",
+ onDelete: ReferentialAction.Cascade);
+ }
+ }
+}
diff --git a/code/api/src/Migrations/AppDbContextModelSnapshot.cs b/code/api/src/Migrations/AppDbContextModelSnapshot.cs
index f2031c1..a266339 100644
--- a/code/api/src/Migrations/AppDbContextModelSnapshot.cs
+++ b/code/api/src/Migrations/AppDbContextModelSnapshot.cs
@@ -17,7 +17,7 @@ namespace IOL.GreatOffice.Api.Migrations
{
#pragma warning disable 612, 618
modelBuilder
- .HasAnnotation("ProductVersion", "6.0.10")
+ .HasAnnotation("ProductVersion", "7.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
@@ -453,7 +453,7 @@ namespace IOL.GreatOffice.Api.Migrations
b.ToTable("customer_groups", (string)null);
});
- modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.ForgotPasswordRequest", b =>
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.PasswordResetRequest", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
@@ -469,12 +469,12 @@ namespace IOL.GreatOffice.Api.Migrations
.HasColumnName("user_id");
b.HasKey("Id")
- .HasName("pk_forgot_password_requests");
+ .HasName("pk_password_reset_requests");
b.HasIndex("UserId")
- .HasDatabaseName("ix_forgot_password_requests_user_id");
+ .HasDatabaseName("ix_password_reset_requests_user_id");
- b.ToTable("forgot_password_requests", (string)null);
+ b.ToTable("password_reset_requests", (string)null);
});
modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.Project", b =>
@@ -744,213 +744,6 @@ namespace IOL.GreatOffice.Api.Migrations
b.ToTable("tenants", (string)null);
});
- modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.TimeCategory", b =>
- {
- b.Property<Guid>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("uuid")
- .HasColumnName("id");
-
- b.Property<string>("Color")
- .HasColumnType("text")
- .HasColumnName("color");
-
- b.Property<DateTime>("CreatedAt")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("created_at");
-
- b.Property<Guid?>("CreatedBy")
- .HasColumnType("uuid")
- .HasColumnName("created_by");
-
- b.Property<bool>("Deleted")
- .HasColumnType("boolean")
- .HasColumnName("deleted");
-
- b.Property<DateTime?>("DeletedAt")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("deleted_at");
-
- b.Property<Guid?>("DeletedBy")
- .HasColumnType("uuid")
- .HasColumnName("deleted_by");
-
- b.Property<DateTime?>("ModifiedAt")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("modified_at");
-
- b.Property<Guid?>("ModifiedBy")
- .HasColumnType("uuid")
- .HasColumnName("modified_by");
-
- b.Property<string>("Name")
- .HasColumnType("text")
- .HasColumnName("name");
-
- b.Property<Guid?>("TenantId")
- .HasColumnType("uuid")
- .HasColumnName("tenant_id");
-
- b.Property<Guid?>("UserId")
- .HasColumnType("uuid")
- .HasColumnName("user_id");
-
- b.HasKey("Id")
- .HasName("pk_time_categories");
-
- b.HasIndex("TenantId")
- .HasDatabaseName("ix_time_categories_tenant_id");
-
- b.HasIndex("UserId")
- .HasDatabaseName("ix_time_categories_user_id");
-
- b.ToTable("time_categories", (string)null);
- });
-
- modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.TimeEntry", b =>
- {
- b.Property<Guid>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("uuid")
- .HasColumnName("id");
-
- b.Property<Guid?>("CategoryId")
- .HasColumnType("uuid")
- .HasColumnName("category_id");
-
- b.Property<DateTime>("CreatedAt")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("created_at");
-
- b.Property<Guid?>("CreatedBy")
- .HasColumnType("uuid")
- .HasColumnName("created_by");
-
- b.Property<bool>("Deleted")
- .HasColumnType("boolean")
- .HasColumnName("deleted");
-
- b.Property<DateTime?>("DeletedAt")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("deleted_at");
-
- b.Property<Guid?>("DeletedBy")
- .HasColumnType("uuid")
- .HasColumnName("deleted_by");
-
- b.Property<string>("Description")
- .HasColumnType("text")
- .HasColumnName("description");
-
- b.Property<DateTime?>("ModifiedAt")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("modified_at");
-
- b.Property<Guid?>("ModifiedBy")
- .HasColumnType("uuid")
- .HasColumnName("modified_by");
-
- b.Property<DateTime>("Start")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("start");
-
- b.Property<DateTime>("Stop")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("stop");
-
- b.Property<Guid?>("TenantId")
- .HasColumnType("uuid")
- .HasColumnName("tenant_id");
-
- b.Property<Guid?>("UserId")
- .HasColumnType("uuid")
- .HasColumnName("user_id");
-
- b.HasKey("Id")
- .HasName("pk_time_entries");
-
- b.HasIndex("CategoryId")
- .HasDatabaseName("ix_time_entries_category_id");
-
- b.HasIndex("TenantId")
- .HasDatabaseName("ix_time_entries_tenant_id");
-
- b.HasIndex("UserId")
- .HasDatabaseName("ix_time_entries_user_id");
-
- b.ToTable("time_entries", (string)null);
- });
-
- modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.TimeLabel", b =>
- {
- b.Property<Guid>("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("uuid")
- .HasColumnName("id");
-
- b.Property<string>("Color")
- .HasColumnType("text")
- .HasColumnName("color");
-
- b.Property<DateTime>("CreatedAt")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("created_at");
-
- b.Property<Guid?>("CreatedBy")
- .HasColumnType("uuid")
- .HasColumnName("created_by");
-
- b.Property<bool>("Deleted")
- .HasColumnType("boolean")
- .HasColumnName("deleted");
-
- b.Property<DateTime?>("DeletedAt")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("deleted_at");
-
- b.Property<Guid?>("DeletedBy")
- .HasColumnType("uuid")
- .HasColumnName("deleted_by");
-
- b.Property<DateTime?>("ModifiedAt")
- .HasColumnType("timestamp with time zone")
- .HasColumnName("modified_at");
-
- b.Property<Guid?>("ModifiedBy")
- .HasColumnType("uuid")
- .HasColumnName("modified_by");
-
- b.Property<string>("Name")
- .HasColumnType("text")
- .HasColumnName("name");
-
- b.Property<Guid?>("TenantId")
- .HasColumnType("uuid")
- .HasColumnName("tenant_id");
-
- b.Property<Guid?>("TimeEntryId")
- .HasColumnType("uuid")
- .HasColumnName("time_entry_id");
-
- b.Property<Guid?>("UserId")
- .HasColumnType("uuid")
- .HasColumnName("user_id");
-
- b.HasKey("Id")
- .HasName("pk_time_labels");
-
- b.HasIndex("TenantId")
- .HasDatabaseName("ix_time_labels_tenant_id");
-
- b.HasIndex("TimeEntryId")
- .HasDatabaseName("ix_time_labels_time_entry_id");
-
- b.HasIndex("UserId")
- .HasDatabaseName("ix_time_labels_user_id");
-
- b.ToTable("time_labels", (string)null);
- });
-
modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.Todo", b =>
{
b.Property<Guid>("Id")
@@ -1528,14 +1321,14 @@ namespace IOL.GreatOffice.Api.Migrations
b.Navigation("OwningUser");
});
- modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.ForgotPasswordRequest", b =>
+ modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.PasswordResetRequest", b =>
{
b.HasOne("IOL.GreatOffice.Api.Data.Database.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
- .HasConstraintName("fk_forgot_password_requests_users_user_id");
+ .HasConstraintName("fk_password_reset_requests_users_user_id");
b.Navigation("User");
});
@@ -1615,69 +1408,6 @@ namespace IOL.GreatOffice.Api.Migrations
b.Navigation("OwningUser");
});
- modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.TimeCategory", b =>
- {
- b.HasOne("IOL.GreatOffice.Api.Data.Database.Tenant", "OwningTenant")
- .WithMany()
- .HasForeignKey("TenantId")
- .HasConstraintName("fk_time_categories_tenants_tenant_id");
-
- b.HasOne("IOL.GreatOffice.Api.Data.Database.User", "OwningUser")
- .WithMany()
- .HasForeignKey("UserId")
- .HasConstraintName("fk_time_categories_users_user_id");
-
- b.Navigation("OwningTenant");
-
- b.Navigation("OwningUser");
- });
-
- modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.TimeEntry", b =>
- {
- b.HasOne("IOL.GreatOffice.Api.Data.Database.TimeCategory", "Category")
- .WithMany()
- .HasForeignKey("CategoryId")
- .HasConstraintName("fk_time_entries_time_categories_category_id");
-
- b.HasOne("IOL.GreatOffice.Api.Data.Database.Tenant", "OwningTenant")
- .WithMany()
- .HasForeignKey("TenantId")
- .HasConstraintName("fk_time_entries_tenants_tenant_id");
-
- b.HasOne("IOL.GreatOffice.Api.Data.Database.User", "OwningUser")
- .WithMany()
- .HasForeignKey("UserId")
- .HasConstraintName("fk_time_entries_users_user_id");
-
- b.Navigation("Category");
-
- b.Navigation("OwningTenant");
-
- b.Navigation("OwningUser");
- });
-
- modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.TimeLabel", b =>
- {
- b.HasOne("IOL.GreatOffice.Api.Data.Database.Tenant", "OwningTenant")
- .WithMany()
- .HasForeignKey("TenantId")
- .HasConstraintName("fk_time_labels_tenants_tenant_id");
-
- b.HasOne("IOL.GreatOffice.Api.Data.Database.TimeEntry", null)
- .WithMany("Labels")
- .HasForeignKey("TimeEntryId")
- .HasConstraintName("fk_time_labels_time_entries_time_entry_id");
-
- b.HasOne("IOL.GreatOffice.Api.Data.Database.User", "OwningUser")
- .WithMany()
- .HasForeignKey("UserId")
- .HasConstraintName("fk_time_labels_users_user_id");
-
- b.Navigation("OwningTenant");
-
- b.Navigation("OwningUser");
- });
-
modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.Todo", b =>
{
b.HasOne("IOL.GreatOffice.Api.Data.Database.User", "AssignedTo")
@@ -1834,11 +1564,6 @@ namespace IOL.GreatOffice.Api.Migrations
b.Navigation("Users");
});
- modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.TimeEntry", b =>
- {
- b.Navigation("Labels");
- });
-
modelBuilder.Entity("IOL.GreatOffice.Api.Data.Database.Todo", b =>
{
b.Navigation("Comments");
diff --git a/code/api/src/Program.cs b/code/api/src/Program.cs
index e56c123..f27f048 100644
--- a/code/api/src/Program.cs
+++ b/code/api/src/Program.cs
@@ -6,15 +6,16 @@ global using System.Net;
global using System.Threading;
global using System.Threading.Tasks;
global using System.Collections.Generic;
-global using System.Runtime.Serialization;
global using System.ComponentModel.DataAnnotations.Schema;
global using System.Security.Claims;
global using System.Text.Json;
global using System.Text.Json.Serialization;
global using IOL.GreatOffice.Api.Data.Database;
-global using IOL.GreatOffice.Api.Data.Exceptions;
global using IOL.GreatOffice.Api.Data.Enums;
global using IOL.GreatOffice.Api.Data.Models;
+global using IOL.GreatOffice.Api.Data.Static;
+global using IOL.GreatOffice.Api.Services;
+global using IOL.GreatOffice.Api.Utilities;
global using IOL.Helpers;
global using Microsoft.OpenApi.Models;
global using Microsoft.AspNetCore.Authentication.Cookies;
@@ -31,10 +32,6 @@ global using Microsoft.Extensions.DependencyInjection;
global using Microsoft.Extensions.Hosting;
global using Microsoft.Extensions.Logging;
global using Serilog;
-global using IOL.GreatOffice.Api.Data;
-global using IOL.GreatOffice.Api.Data.Static;
-global using IOL.GreatOffice.Api.Services;
-global using IOL.GreatOffice.Api.Utilities;
using IOL.GreatOffice.Api.Endpoints.V1;
using IOL.GreatOffice.Api.Jobs;
using Microsoft.AspNetCore.HttpOverrides;
@@ -228,11 +225,6 @@ public static class Program
CreateWebApplication(CreateAppBuilder(args)).Run();
return 0;
} catch (Exception ex) {
- // This is subject to change in future .net versions, see https://github.com/dotnet/runtime/issues/60600.
- if (ex.GetType().Name.Equals("StopTheHostException", StringComparison.Ordinal)) {
- throw;
- }
-
Log.Fatal(ex, "Unhandled exception");
return 1;
}
diff --git a/code/api/src/Services/PasswordResetService.cs b/code/api/src/Services/PasswordResetService.cs
index 76eb2fe..d4889bc 100644
--- a/code/api/src/Services/PasswordResetService.cs
+++ b/code/api/src/Services/PasswordResetService.cs
@@ -19,8 +19,8 @@ public class PasswordResetService
_mailService = mailService;
}
- public async Task<ForgotPasswordRequest> GetRequestAsync(Guid id, CancellationToken cancellationToken = default) {
- var request = await _database.ForgotPasswordRequests
+ public async Task<PasswordResetRequest> GetRequestAsync(Guid id, CancellationToken cancellationToken = default) {
+ var request = await _database.PasswordResetRequests
.Include(c => c.User)
.SingleOrDefaultAsync(c => c.Id == id, cancellationToken);
if (request == default) {
@@ -30,31 +30,24 @@ public class PasswordResetService
_logger.LogInformation($"Found password reset request for user: {request.User.Username}, expires at {request.ExpirationDate} (in {request.ExpirationDate.Subtract(AppDateTime.UtcNow).Minutes} minutes).");
return request;
}
-
- public async Task<bool> FullFillRequestAsync(Guid id, string newPassword, CancellationToken cancellationToken = default) {
- var request = await GetRequestAsync(id, cancellationToken);
- if (request == default) {
- throw new ForgotPasswordRequestNotFoundException("Request with id: " + id + " was not found");
- }
-
- var user = _database.Users.SingleOrDefault(c => c.Id == request.User.Id);
- if (user == default) {
- throw new UserNotFoundException("User with id: " + request.User.Id + " was not found");
- }
+ public async Task<FulfillPasswordResetRequestResult> FulfillRequestAsync(Guid id, string newPassword, CancellationToken cancellationToken = default) {
+ var request = await GetRequestAsync(id, cancellationToken);
+ if (request == default) return FulfillPasswordResetRequestResult.REQUEST_NOT_FOUND;
+ var user = _database.Users.FirstOrDefault(c => c.Id == request.User.Id);
+ if (user == default) return FulfillPasswordResetRequestResult.USER_NOT_FOUND;
user.HashAndSetPassword(newPassword);
_database.Users.Update(user);
await _database.SaveChangesAsync(cancellationToken);
_logger.LogInformation($"Fullfilled password reset request for user: {request.User.Username}");
await DeleteRequestsForUserAsync(user.Id, cancellationToken);
- return true;
+ return FulfillPasswordResetRequestResult.FULFILLED;
}
-
public async Task AddRequestAsync(User user, TimeZoneInfo requestTz, CancellationToken cancellationToken = default) {
await DeleteRequestsForUserAsync(user.Id, cancellationToken);
- var request = new ForgotPasswordRequest(user);
- _database.ForgotPasswordRequests.Add(request);
+ var request = new PasswordResetRequest(user);
+ _database.PasswordResetRequests.Add(request);
await _database.SaveChangesAsync(cancellationToken);
var portalUrl = _configuration.PORTAL_URL;
var emailFromAddress = _configuration.EMAIL_FROM_ADDRESS;
@@ -88,22 +81,21 @@ If you did not request a password reset, no action is required.
}
public async Task DeleteRequestsForUserAsync(Guid userId, CancellationToken cancellationToken = default) {
- var requestsToRemove = _database.ForgotPasswordRequests.Where(c => c.UserId == userId).ToList();
+ var requestsToRemove = _database.PasswordResetRequests.Where(c => c.UserId == userId).ToList();
if (!requestsToRemove.Any()) return;
- _database.ForgotPasswordRequests.RemoveRange(requestsToRemove);
+ _database.PasswordResetRequests.RemoveRange(requestsToRemove);
await _database.SaveChangesAsync(cancellationToken);
_logger.LogInformation($"Deleted {requestsToRemove.Count} password reset requests for user: {userId}.");
}
-
public async Task DeleteStaleRequestsAsync(CancellationToken cancellationToken = default) {
var deleteCount = 0;
- foreach (var request in _database.ForgotPasswordRequests.Where(c => c.IsExpired)) {
+ foreach (var request in _database.PasswordResetRequests.Where(c => c.IsExpired)) {
if (!request.IsExpired) {
continue;
}
- _database.ForgotPasswordRequests.Remove(request);
+ _database.PasswordResetRequests.Remove(request);
deleteCount++;
_logger.LogInformation($"Marking password reset request with id: {request.Id} for deletion, expiration date was {request.ExpirationDate}.");
}