aboutsummaryrefslogtreecommitdiffstats
path: root/code/api/src/Models/Database
diff options
context:
space:
mode:
authorivarlovlie <git@ivarlovlie.no>2023-02-25 13:15:44 +0100
committerivarlovlie <git@ivarlovlie.no>2023-02-25 13:15:44 +0100
commit900bb5e845c3ad44defbd427cae3d44a4a43321f (patch)
treedf3d96a93771884add571e82336c29fc3d9c7a1c /code/api/src/Models/Database
downloadgreatoffice-900bb5e845c3ad44defbd427cae3d44a4a43321f.tar.xz
greatoffice-900bb5e845c3ad44defbd427cae3d44a4a43321f.zip
feat: Initial commit
Diffstat (limited to 'code/api/src/Models/Database')
-rw-r--r--code/api/src/Models/Database/Api/ApiAccessToken.cs12
-rw-r--r--code/api/src/Models/Database/Base.cs22
-rw-r--r--code/api/src/Models/Database/BaseWithOwner.cs40
-rw-r--r--code/api/src/Models/Database/Customer/Customer.cs29
-rw-r--r--code/api/src/Models/Database/Customer/CustomerContact.cs12
-rw-r--r--code/api/src/Models/Database/Customer/CustomerEvent.cs8
-rw-r--r--code/api/src/Models/Database/Customer/CustomerGroup.cs7
-rw-r--r--code/api/src/Models/Database/Customer/CustomerGroupMembership.cs7
-rw-r--r--code/api/src/Models/Database/Internal/PasswordResetRequest.cs23
-rw-r--r--code/api/src/Models/Database/Internal/Tenant.cs12
-rw-r--r--code/api/src/Models/Database/Internal/User.cs44
-rw-r--r--code/api/src/Models/Database/MainAppDatabase.cs107
-rw-r--r--code/api/src/Models/Database/Project/Project.cs15
-rw-r--r--code/api/src/Models/Database/Project/ProjectLabel.cs8
-rw-r--r--code/api/src/Models/Database/Project/ProjectMember.cs8
-rw-r--r--code/api/src/Models/Database/Queues/ValidationEmail.cs8
-rw-r--r--code/api/src/Models/Database/Todo/Todo.cs17
-rw-r--r--code/api/src/Models/Database/Todo/TodoCollection.cs11
-rw-r--r--code/api/src/Models/Database/Todo/TodoCollectionAccessControl.cs12
-rw-r--r--code/api/src/Models/Database/Todo/TodoComment.cs8
-rw-r--r--code/api/src/Models/Database/Todo/TodoLabel.cs8
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; }
+}