diff options
Diffstat (limited to 'code/api/src/Models/Database')
21 files changed, 418 insertions, 0 deletions
diff --git a/code/api/src/Models/Database/Api/ApiAccessToken.cs b/code/api/src/Models/Database/Api/ApiAccessToken.cs new file mode 100644 index 0000000..9359fc4 --- /dev/null +++ b/code/api/src/Models/Database/Api/ApiAccessToken.cs @@ -0,0 +1,12 @@ +namespace IOL.GreatOffice.Api.Data.Database; + +public class ApiAccessToken : Base +{ + public User User { get; set; } + public DateTime ExpiryDate { get; set; } + public bool AllowRead { get; set; } + public bool AllowCreate { get; set; } + public bool AllowUpdate { get; set; } + public bool AllowDelete { get; set; } + public bool HasExpired => ExpiryDate < AppDateTime.UtcNow; +}
\ No newline at end of file diff --git a/code/api/src/Models/Database/Base.cs b/code/api/src/Models/Database/Base.cs new file mode 100644 index 0000000..0b16b12 --- /dev/null +++ b/code/api/src/Models/Database/Base.cs @@ -0,0 +1,22 @@ +namespace IOL.GreatOffice.Api.Data.Database; + +public abstract class Base +{ + protected Base() { + Id = Guid.NewGuid(); + CreatedAt = AppDateTime.UtcNow; + } + + public Guid Id { get; init; } + public DateTime CreatedAt { get; init; } + public DateTime? ModifiedAt { get; private set; } + public DateTime? DeletedAt { get; private set; } + public bool Deleted { get; private set; } + + public void SetModified() => ModifiedAt = AppDateTime.UtcNow; + + public void SetDeleted() { + Deleted = true; + DeletedAt = AppDateTime.UtcNow; + } +}
\ No newline at end of file diff --git a/code/api/src/Models/Database/BaseWithOwner.cs b/code/api/src/Models/Database/BaseWithOwner.cs new file mode 100644 index 0000000..7e9f6c1 --- /dev/null +++ b/code/api/src/Models/Database/BaseWithOwner.cs @@ -0,0 +1,40 @@ +namespace IOL.GreatOffice.Api.Data.Database; + +/// <summary> +/// Base class for all entities with ownership. +/// </summary> +public abstract class BaseWithOwner : Base +{ + protected BaseWithOwner() { } + + protected BaseWithOwner(Guid createdBy) { + CreatedBy = createdBy; + } + + protected BaseWithOwner(LoggedInUserModel loggedInUser) { + CreatedBy = loggedInUser.Id; + } + + public Guid? UserId { get; private set; } + public Guid? TenantId { get; private set; } + public Guid? ModifiedBy { get; private set; } + public Guid? CreatedBy { get; private set; } + public Guid? DeletedBy { get; private set; } + public User OwningUser { get; set; } + public Tenant OwningTenant { get; set; } + + public void SetDeleted(Guid userId) { + DeletedBy = userId; + base.SetDeleted(); + } + + public void SetModified(Guid userId) { + ModifiedBy = userId; + base.SetModified(); + } + + public void SetOwnerIds(Guid userId = default, Guid tenantId = default) { + if (tenantId != default) TenantId = tenantId; + if (userId != default) UserId = userId; + } +}
\ No newline at end of file diff --git a/code/api/src/Models/Database/Customer/Customer.cs b/code/api/src/Models/Database/Customer/Customer.cs new file mode 100644 index 0000000..8e153c6 --- /dev/null +++ b/code/api/src/Models/Database/Customer/Customer.cs @@ -0,0 +1,29 @@ +namespace IOL.GreatOffice.Api.Data.Database; + +public class Customer : BaseWithOwner +{ + public Customer() { } + public Customer(LoggedInUserModel loggedInUserModel) : base(loggedInUserModel) { } + + public string CustomerNumber { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string Address1 { get; set; } + public string Address2 { get; set; } + public string PostalCode { get; set; } + public string PostalCity { get; set; } + public string Country { get; set; } + public string Phone { get; set; } + public string Email { get; set; } + public string VATNumber { get; set; } + public string ORGNumber { get; set; } + public string DefaultReference { get; set; } + public string Website { get; set; } + public string Currency { get; set; } + public Guid? OwnerId { get; set; } + public User Owner { get; set; } + public ICollection<CustomerGroup> Groups { get; set; } + public ICollection<CustomerContact> Contacts { get; set; } + public ICollection<CustomerEvent> Events { get; set; } + public ICollection<Project> Projects { get; set; } +}
\ No newline at end of file diff --git a/code/api/src/Models/Database/Customer/CustomerContact.cs b/code/api/src/Models/Database/Customer/CustomerContact.cs new file mode 100644 index 0000000..f5a951d --- /dev/null +++ b/code/api/src/Models/Database/Customer/CustomerContact.cs @@ -0,0 +1,12 @@ +namespace IOL.GreatOffice.Api.Data.Database; + +public class CustomerContact : BaseWithOwner +{ + public Customer Customer { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public string Email { get; set; } + public string Phone { get; set; } + public string WorkTitle { get; set; } + public string Note { get; set; } +} diff --git a/code/api/src/Models/Database/Customer/CustomerEvent.cs b/code/api/src/Models/Database/Customer/CustomerEvent.cs new file mode 100644 index 0000000..a87da4c --- /dev/null +++ b/code/api/src/Models/Database/Customer/CustomerEvent.cs @@ -0,0 +1,8 @@ +namespace IOL.GreatOffice.Api.Data.Database; + +public class CustomerEvent : BaseWithOwner +{ + public Customer Customer { get; set; } + public string Title { get; set; } + public string Note { get; set; } +} diff --git a/code/api/src/Models/Database/Customer/CustomerGroup.cs b/code/api/src/Models/Database/Customer/CustomerGroup.cs new file mode 100644 index 0000000..9438f3c --- /dev/null +++ b/code/api/src/Models/Database/Customer/CustomerGroup.cs @@ -0,0 +1,7 @@ +namespace IOL.GreatOffice.Api.Data.Database; + +public class CustomerGroup : BaseWithOwner +{ + public string Name { get; set; } + public ICollection<Customer> Customers { get; set; } +}
\ No newline at end of file diff --git a/code/api/src/Models/Database/Customer/CustomerGroupMembership.cs b/code/api/src/Models/Database/Customer/CustomerGroupMembership.cs new file mode 100644 index 0000000..ec0d4af --- /dev/null +++ b/code/api/src/Models/Database/Customer/CustomerGroupMembership.cs @@ -0,0 +1,7 @@ +namespace IOL.GreatOffice.Api.Data.Database; + +public class CustomerGroupMembership : Base +{ + public Customer Customer { get; set; } + public CustomerGroup Group { get; set; } +}
\ No newline at end of file diff --git a/code/api/src/Models/Database/Internal/PasswordResetRequest.cs b/code/api/src/Models/Database/Internal/PasswordResetRequest.cs new file mode 100644 index 0000000..ee73fd2 --- /dev/null +++ b/code/api/src/Models/Database/Internal/PasswordResetRequest.cs @@ -0,0 +1,23 @@ +namespace IOL.GreatOffice.Api.Data.Database; + +public class PasswordResetRequest +{ + public PasswordResetRequest() { } + + public PasswordResetRequest(User user) { + CreatedAt = AppDateTime.UtcNow; + Id = Guid.NewGuid(); + User = user; + } + + public Guid Id { get; set; } + public Guid UserId { get; set; } + public User User { get; set; } + public DateTime CreatedAt { get; set; } + + [NotMapped] + public DateTime ExpirationDate => CreatedAt.AddMinutes(15); + + [NotMapped] + public bool IsExpired => DateTime.Compare(ExpirationDate, AppDateTime.UtcNow) < 0; +} diff --git a/code/api/src/Models/Database/Internal/Tenant.cs b/code/api/src/Models/Database/Internal/Tenant.cs new file mode 100644 index 0000000..471164d --- /dev/null +++ b/code/api/src/Models/Database/Internal/Tenant.cs @@ -0,0 +1,12 @@ +namespace IOL.GreatOffice.Api.Data.Database; + +public class Tenant : BaseWithOwner +{ + public string Slug { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string ContactEmail { get; set; } + public Guid MasterUserId { get; set; } + public string MasterUserPassword { get; set; } + public ICollection<User> Users { get; set; } +}
\ No newline at end of file diff --git a/code/api/src/Models/Database/Internal/User.cs b/code/api/src/Models/Database/Internal/User.cs new file mode 100644 index 0000000..f4d08ff --- /dev/null +++ b/code/api/src/Models/Database/Internal/User.cs @@ -0,0 +1,44 @@ +namespace IOL.GreatOffice.Api.Data.Database; + +public class User : Base +{ + public User() { } + + public User(string username) { + Username = username; + } + + public string FirstName { get; set; } + public string LastName { get; set; } + public string Email { get; set; } + public string Username { get; set; } + public string Password { get; set; } + public DateTime EmailLastValidated { get; set; } + public ICollection<Tenant> Tenants { get; set; } + public Guid? DeletedBy { get; set; } + + public string DisplayName(bool isForGreeting = false) { + if (!isForGreeting && FirstName.HasValue() && LastName.HasValue()) return FirstName + " " + LastName; + return FirstName.HasValue() ? FirstName : Username ?? Email; + } + + public void HashAndSetPassword(string password) { + Password = PasswordHelper.HashPassword(password); + } + + public bool VerifyPassword(string password) { + return PasswordHelper.Verify(password, Password); + } + + public void SetDeleted(Guid userId) { + base.SetDeleted(); + DeletedBy = userId; + } + + public IEnumerable<Claim> DefaultClaims() { + return new Claim[] { + new(AppClaims.USER_ID, Id.ToString()), + new(AppClaims.NAME, Username), + }; + } +}
\ No newline at end of file diff --git a/code/api/src/Models/Database/MainAppDatabase.cs b/code/api/src/Models/Database/MainAppDatabase.cs new file mode 100644 index 0000000..2b42fe0 --- /dev/null +++ b/code/api/src/Models/Database/MainAppDatabase.cs @@ -0,0 +1,107 @@ +using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore; + +namespace IOL.GreatOffice.Api.Data.Database; + +public class MainAppDatabase : DbContext, IDataProtectionKeyContext +{ + public MainAppDatabase(DbContextOptions<MainAppDatabase> options) : base(options) { } + public DbSet<User> Users { 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; } + public DbSet<Project> Projects { get; set; } + public DbSet<ProjectLabel> ProjectLabels { get; set; } + public DbSet<Customer> Customers { get; set; } + public DbSet<CustomerContact> CustomersContacts { get; set; } + public DbSet<CustomerEvent> CustomerEvents { get; set; } + public DbSet<CustomerGroup> CustomerGroups { get; set; } + public DbSet<TodoLabel> TodoLabels { get; set; } + public DbSet<TodoCollectionAccessControl> TodoProjectAccessControls { get; set; } + public DbSet<TodoCollection> TodoProjects { get; set; } + public DbSet<TodoComment> TodoComments { get; set; } + public DbSet<Todo> Todos { get; set; } + public DbSet<ValidationEmail> ValidationEmails { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) { + modelBuilder.Entity<User>(e => { + e.HasMany(n => n.Tenants); + e.ToTable("users"); + }); + modelBuilder.Entity<PasswordResetRequest>(e => { + e.HasOne(c => c.User); + e.ToTable("password_reset_requests"); + }); + modelBuilder.Entity<ApiAccessToken>(e => { + e.HasOne(n => n.User); + e.ToTable("api_access_tokens"); + }); + modelBuilder.Entity<Tenant>(e => { + e.HasMany(n => n.Users); + e.ToTable("tenants"); + }); + modelBuilder.Entity<Project>(e => { + e.HasMany(n => n.Members); + e.HasMany(n => n.Customers); + e.ToTable("projects"); + }); + modelBuilder.Entity<ProjectMember>(e => { + e.HasOne(n => n.Project); + e.HasOne(n => n.User); + e.ToTable("project_members"); + }); + modelBuilder.Entity<ProjectLabel>(e => { + e.HasOne(n => n.Project); + e.ToTable("project_labels"); + }); + modelBuilder.Entity<Customer>(e => { + e.HasOne(n => n.Owner); + e.HasMany(n => n.Events); + e.HasMany(n => n.Contacts); + e.HasMany(n => n.Groups); + e.HasMany(n => n.Projects); + e.ToTable("customers"); + }); + modelBuilder.Entity<CustomerContact>(e => { + e.HasOne(n => n.Customer); + e.ToTable("customer_contacts"); + }); + modelBuilder.Entity<CustomerEvent>(e => { + e.HasOne(n => n.Customer); + e.ToTable("customer_events"); + }); + modelBuilder.Entity<CustomerGroup>(e => { + e.HasMany(n => n.Customers); + e.ToTable("customer_groups"); + }); + modelBuilder.Entity<Todo>(e => { + e.HasOne(n => n.Collection); + e.HasOne(n => n.AssignedTo); + e.HasOne(n => n.ClosedBy); + e.HasMany(n => n.Labels); + e.HasMany(n => n.Comments); + e.ToTable("todos"); + }); + modelBuilder.Entity<TodoCollection>(e => { + e.HasOne(n => n.Project); + e.HasMany(n => n.AccessControls); + e.ToTable("todo_collections"); + }); + modelBuilder.Entity<TodoComment>(e => { + e.HasOne(n => n.Todo); + e.ToTable("todo_comments"); + }); + modelBuilder.Entity<TodoLabel>(e => { + e.HasOne(n => n.Todo); + e.ToTable("todo_labels"); + }); + modelBuilder.Entity<TodoCollectionAccessControl>(e => { + e.HasOne(n => n.User); + e.HasOne(n => n.Collection); + e.ToTable("todo_collection_access_controls"); + }); + modelBuilder.Entity<ValidationEmail>(e => { e.ToTable("validation_emails"); }); + + base.OnModelCreating(modelBuilder); + } +}
\ No newline at end of file diff --git a/code/api/src/Models/Database/Project/Project.cs b/code/api/src/Models/Database/Project/Project.cs new file mode 100644 index 0000000..de9e2cb --- /dev/null +++ b/code/api/src/Models/Database/Project/Project.cs @@ -0,0 +1,15 @@ +namespace IOL.GreatOffice.Api.Data.Database; + +public class Project : BaseWithOwner +{ + public Project() { } + + public Project(LoggedInUserModel loggedInUserModel) : base(loggedInUserModel) { } + public string Name { get; set; } + public string Description { get; set; } + public DateTime? Start { get; set; } + public DateTime? Stop { get; set; } + public ICollection<Customer> Customers { get; set; } + public ICollection<ProjectMember> Members { get; set; } + public ICollection<ProjectLabel> Labels { get; set; } +}
\ No newline at end of file diff --git a/code/api/src/Models/Database/Project/ProjectLabel.cs b/code/api/src/Models/Database/Project/ProjectLabel.cs new file mode 100644 index 0000000..0e1dc5d --- /dev/null +++ b/code/api/src/Models/Database/Project/ProjectLabel.cs @@ -0,0 +1,8 @@ +namespace IOL.GreatOffice.Api.Data.Database; + +public class ProjectLabel : BaseWithOwner +{ + public string Value { get; set; } + public string Color { get; set; } + public Project Project { get; set; } +} diff --git a/code/api/src/Models/Database/Project/ProjectMember.cs b/code/api/src/Models/Database/Project/ProjectMember.cs new file mode 100644 index 0000000..a5e0682 --- /dev/null +++ b/code/api/src/Models/Database/Project/ProjectMember.cs @@ -0,0 +1,8 @@ +namespace IOL.GreatOffice.Api.Data.Database; + +public class ProjectMember : Base +{ + public Project Project { get; set; } + public User User { get; set; } + public ProjectRole Role { get; set; } +}
\ No newline at end of file diff --git a/code/api/src/Models/Database/Queues/ValidationEmail.cs b/code/api/src/Models/Database/Queues/ValidationEmail.cs new file mode 100644 index 0000000..0457768 --- /dev/null +++ b/code/api/src/Models/Database/Queues/ValidationEmail.cs @@ -0,0 +1,8 @@ +namespace IOL.GreatOffice.Api.Data.Database; + +public class ValidationEmail +{ + public Guid Id { get; set; } + public DateTime EmailSentAt { get; set; } + public Guid UserId { get; set; } +}
\ No newline at end of file diff --git a/code/api/src/Models/Database/Todo/Todo.cs b/code/api/src/Models/Database/Todo/Todo.cs new file mode 100644 index 0000000..2d7f109 --- /dev/null +++ b/code/api/src/Models/Database/Todo/Todo.cs @@ -0,0 +1,17 @@ +namespace IOL.GreatOffice.Api.Data.Database; + +public class Todo : BaseWithOwner +{ + public string PublicId { get; set; } + public Guid? AssignedToId { get; set; } + public Guid? ClosedById { get; set; } + public Guid CollectionId { get; set; } + public DateTime? ClosedAt { get; set; } + public string Title { get; set; } + public string Description { get; set; } + public ICollection<TodoLabel> Labels { get; set; } + public ICollection<TodoComment> Comments { get; set; } + public User AssignedTo { get; set; } + public User ClosedBy { get; set; } + public TodoCollection Collection { get; set; } +}
\ No newline at end of file diff --git a/code/api/src/Models/Database/Todo/TodoCollection.cs b/code/api/src/Models/Database/Todo/TodoCollection.cs new file mode 100644 index 0000000..470e5e7 --- /dev/null +++ b/code/api/src/Models/Database/Todo/TodoCollection.cs @@ -0,0 +1,11 @@ +namespace IOL.GreatOffice.Api.Data.Database; + +public class TodoCollection : BaseWithOwner +{ + public string Name { get; set; } + public string Description { get; set; } + public TodoVisibility Visibility { get; set; } + public Guid? ProjectId { get; set; } + public Project Project { get; set; } + public ICollection<TodoCollectionAccessControl> AccessControls { get; set; } +}
\ No newline at end of file diff --git a/code/api/src/Models/Database/Todo/TodoCollectionAccessControl.cs b/code/api/src/Models/Database/Todo/TodoCollectionAccessControl.cs new file mode 100644 index 0000000..1676c06 --- /dev/null +++ b/code/api/src/Models/Database/Todo/TodoCollectionAccessControl.cs @@ -0,0 +1,12 @@ +namespace IOL.GreatOffice.Api.Data.Database; + +public class TodoCollectionAccessControl : Base +{ + public TodoCollection Collection { get; set; } + public User User { get; set; } + public Guid? UserId { get; set; } + public bool CanBrowse { get; set; } + public bool CanSubmit { get; set; } + public bool CanComment { get; set; } + public bool CanEdit { get; set; } +}
\ No newline at end of file diff --git a/code/api/src/Models/Database/Todo/TodoComment.cs b/code/api/src/Models/Database/Todo/TodoComment.cs new file mode 100644 index 0000000..32ac3a3 --- /dev/null +++ b/code/api/src/Models/Database/Todo/TodoComment.cs @@ -0,0 +1,8 @@ +namespace IOL.GreatOffice.Api.Data.Database; + +public class TodoComment : BaseWithOwner +{ + public string Value { get; set; } + public Todo Todo { get; set; } + public TodoClosingStatement? ClosingStatement { get; set; } +}
\ No newline at end of file diff --git a/code/api/src/Models/Database/Todo/TodoLabel.cs b/code/api/src/Models/Database/Todo/TodoLabel.cs new file mode 100644 index 0000000..7753ade --- /dev/null +++ b/code/api/src/Models/Database/Todo/TodoLabel.cs @@ -0,0 +1,8 @@ +namespace IOL.GreatOffice.Api.Data.Database; + +public class TodoLabel : BaseWithOwner +{ + public string Name { get; set; } + public string Color { get; set; } + public Todo Todo { get; set; } +} |
