diff options
Diffstat (limited to 'code/api/src/Models')
40 files changed, 754 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; } +} diff --git a/code/api/src/Models/Enums/FulfillPasswordResetRequestResult.cs b/code/api/src/Models/Enums/FulfillPasswordResetRequestResult.cs new file mode 100644 index 0000000..2a84c48 --- /dev/null +++ b/code/api/src/Models/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/Models/Enums/PasswordResetRequestStatus.cs b/code/api/src/Models/Enums/PasswordResetRequestStatus.cs new file mode 100644 index 0000000..5629e6f --- /dev/null +++ b/code/api/src/Models/Enums/PasswordResetRequestStatus.cs @@ -0,0 +1,6 @@ +namespace IOL.GreatOffice.Api.Data.Enums; + +public enum PasswordResetRequestStatus +{ + +}
\ No newline at end of file diff --git a/code/api/src/Models/Enums/ProjectRole.cs b/code/api/src/Models/Enums/ProjectRole.cs new file mode 100644 index 0000000..c4a3f29 --- /dev/null +++ b/code/api/src/Models/Enums/ProjectRole.cs @@ -0,0 +1,9 @@ +namespace IOL.GreatOffice.Api.Data.Enums; + +public enum ProjectRole +{ + EXTERNAL, + RESOURCE, + LEADER, + OWNER +}
\ No newline at end of file diff --git a/code/api/src/Models/Enums/TodoClosingStatement.cs b/code/api/src/Models/Enums/TodoClosingStatement.cs new file mode 100644 index 0000000..d838031 --- /dev/null +++ b/code/api/src/Models/Enums/TodoClosingStatement.cs @@ -0,0 +1,13 @@ +namespace IOL.GreatOffice.Api.Data.Enums; + +public enum TodoClosingStatement +{ + REPORTED, + RESOLVED, + FIXED, + IMPLEMENTED, + WONT_FIX, + BY_DESIGN, + INVALID, + DUPLICATE +}
\ No newline at end of file diff --git a/code/api/src/Models/Enums/TodoVisibility.cs b/code/api/src/Models/Enums/TodoVisibility.cs new file mode 100644 index 0000000..2c8fa83 --- /dev/null +++ b/code/api/src/Models/Enums/TodoVisibility.cs @@ -0,0 +1,10 @@ + +namespace IOL.GreatOffice.Api.Data.Enums; + +public enum TodoVisibility +{ + PRIVATE = 0, + UNLISTED = 1, + INTERNAL = 2, + PUBLIC = 3, +}
\ No newline at end of file diff --git a/code/api/src/Models/Misc/ApiSpecDocument.cs b/code/api/src/Models/Misc/ApiSpecDocument.cs new file mode 100644 index 0000000..1c7d936 --- /dev/null +++ b/code/api/src/Models/Misc/ApiSpecDocument.cs @@ -0,0 +1,9 @@ +namespace IOL.GreatOffice.Api.Data.Models; + +public class ApiSpecDocument +{ + public string VersionName { get; set; } + public string SwaggerPath { get; set; } + public ApiVersion Version { get; set; } + public OpenApiInfo OpenApiInfo { get; set; } +} diff --git a/code/api/src/Models/Misc/AppConfiguration.cs b/code/api/src/Models/Misc/AppConfiguration.cs new file mode 100644 index 0000000..31e5726 --- /dev/null +++ b/code/api/src/Models/Misc/AppConfiguration.cs @@ -0,0 +1,118 @@ +using System.Security.Cryptography.X509Certificates; + +namespace IOL.GreatOffice.Api.Data.Models; + +public class AppConfiguration +{ + /// <summary> + /// An reachable ip address or url that points to a postgres database. + /// </summary> + public string DB_HOST { get; set; } + + /// <summary> + /// The port number to use with DB_HOST to point to the postgres database. + /// </summary> + public string DB_PORT { get; set; } + + /// <summary> + /// The database user to authenticate against postgres with. + /// </summary> + public string DB_USER { get; set; } + + /// <summary> + /// The password for the database user to authenticate against postgres with. + /// </summary> + public string DB_PASSWORD { get; set; } + + /// <summary> + /// The name of the main app database. + /// </summary> + public string DB_NAME { get; set; } + + /// <summary> + /// An reachable ip address or url that points to a postgres(quartz) database. + /// </summary> + public string QUARTZ_DB_HOST { get; set; } + + /// <summary> + /// The port number to use with QUARTZ_DB_HOST to point to the postgres(quartz) database. + /// </summary> + public string QUARTZ_DB_PORT { get; set; } + + /// <summary> + /// The database user to authenticate against postgres(quartz) with. + /// </summary> + public string QUARTZ_DB_USER { get; set; } + + /// <summary> + /// The password for the database user to authenticate against postgres(quartz) with. + /// </summary> + public string QUARTZ_DB_PASSWORD { get; set; } + + /// <summary> + /// The name of the quartz database. + /// </summary> + public string QUARTZ_DB_NAME { get; set; } + + /// <summary> + /// API key to use when pushing logs to SEQ + /// </summary> + public string SEQ_API_KEY { get; set; } + + /// <summary> + /// Url pointing to the seq instance that processes server logs + /// </summary> + public string SEQ_API_URL { get; set; } + + /// <summary> + /// A token used when sending email via Postmakr + /// </summary> + public string POSTMARK_TOKEN { get; set; } + + /// <summary> + /// The address to send emails from, needs to be setup as a sender in postmark + /// </summary> + public string EMAIL_FROM_ADDRESS { get; set; } + + /// <summary> + /// The absolute url to the frontend app + /// </summary> + public string CANONICAL_FRONTEND_URL { get; set; } + + /// <summary> + /// The absolute url to the backend api + /// </summary> + public string CANONICAL_BACKEND_URL { get; set; } + + /// <summary> + /// A random string used to encrypt/decrypt for general purposes + /// </summary> + public string APP_AES_KEY { get; set; } + + /// <summary> + /// A base64 string containing a passwordless pfx cert + /// </summary> + public string APP_CERT { get; set; } + + public X509Certificate2 CERT1() => new(Convert.FromBase64String(APP_CERT)); + + public object GetPublicVersion() { + return new { + DB_HOST, + DB_PORT, + DB_USER, + DB_PASSWORD = DB_PASSWORD.Obfuscate() ?? "", + QUARTZ_DB_HOST, + QUARTZ_DB_PORT, + QUARTZ_DB_USER, + QUARTZ_DB_PASSWORD = QUARTZ_DB_PASSWORD.Obfuscate() ?? "", + SEQ_API_KEY = SEQ_API_KEY.Obfuscate() ?? "", + SEQ_API_URL, + POSTMARK_TOKEN = POSTMARK_TOKEN.Obfuscate() ?? "", + EMAIL_FROM_ADDRESS, + APP_AES_KEY = APP_AES_KEY.Obfuscate() ?? "", + CERT1 = CERT1().PublicKey.Oid.FriendlyName, + CANONICAL_FRONTEND_URL + }; + } +}
\ No newline at end of file diff --git a/code/api/src/Models/Misc/AppPath.cs b/code/api/src/Models/Misc/AppPath.cs new file mode 100644 index 0000000..e47e48c --- /dev/null +++ b/code/api/src/Models/Misc/AppPath.cs @@ -0,0 +1,23 @@ +namespace IOL.GreatOffice.Api.Data.Models; + +public sealed record AppPath +{ + public string HostPath { get; init; } + public string WebPath { get; init; } + + public string GetHostPathForFilename(string filename, string fallback = "") { + if (filename.IsNullOrWhiteSpace()) { + return fallback; + } + + return Path.Combine(HostPath, filename); + } + + public string GetWebPathForFilename(string filename, string fallback = "") { + if (filename.IsNullOrWhiteSpace()) { + return fallback; + } + + return Path.Combine(WebPath, filename); + } +} diff --git a/code/api/src/Models/Misc/KnownProblemModel.cs b/code/api/src/Models/Misc/KnownProblemModel.cs new file mode 100644 index 0000000..9acc85c --- /dev/null +++ b/code/api/src/Models/Misc/KnownProblemModel.cs @@ -0,0 +1,26 @@ +namespace IOL.GreatOffice.Api.Data.Models; + +public class KnownProblemModel +{ + public KnownProblemModel(string title = default, string subtitle = default, Dictionary<string, string[]> errors = default) { + Title = title; + Subtitle = subtitle; + Errors = errors ?? new(); + } + + public string Title { get; set; } + public string Subtitle { get; set; } + public Dictionary<string, string[]> Errors { get; set; } + public string TraceId { get; set; } + + public void AddError(string field, string errorText) { + if (!Errors.ContainsKey(field)) { + Errors.Add(field, new[] {errorText}); + } else { + var currentErrors = Errors[field]; + var newErrors = currentErrors.Concat(new[] {errorText}); + Errors.Remove(field); + Errors.Add(field, newErrors.ToArray()); + } + } +}
\ No newline at end of file diff --git a/code/api/src/Models/Misc/LoggedInUserModel.cs b/code/api/src/Models/Misc/LoggedInUserModel.cs new file mode 100644 index 0000000..541d4a5 --- /dev/null +++ b/code/api/src/Models/Misc/LoggedInUserModel.cs @@ -0,0 +1,8 @@ +namespace IOL.GreatOffice.Api.Data.Models; + +public class LoggedInUserModel +{ + public Guid Id { get; set; } + public string Username { get; set; } + public Guid TenantId { get; set; } +}
\ No newline at end of file diff --git a/code/api/src/Models/Misc/RequestTimeZoneInfo.cs b/code/api/src/Models/Misc/RequestTimeZoneInfo.cs new file mode 100644 index 0000000..4c5aa13 --- /dev/null +++ b/code/api/src/Models/Misc/RequestTimeZoneInfo.cs @@ -0,0 +1,8 @@ +namespace IOL.GreatOffice.Api.Data.Models; + +public class RequestTimeZoneInfo +{ + public TimeZoneInfo TimeZoneInfo { get; set; } + public int Offset { get; set; } + public DateTime LocalDateTime { get; set; } +}
\ No newline at end of file diff --git a/code/api/src/Models/Static/AppClaims.cs b/code/api/src/Models/Static/AppClaims.cs new file mode 100644 index 0000000..6569700 --- /dev/null +++ b/code/api/src/Models/Static/AppClaims.cs @@ -0,0 +1,7 @@ +namespace IOL.GreatOffice.Api.Data.Static; + +public static class AppClaims +{ + public const string USER_ID = "user_id"; + public const string NAME = "name"; +} diff --git a/code/api/src/Models/Static/AppConstants.cs b/code/api/src/Models/Static/AppConstants.cs new file mode 100644 index 0000000..d0a888b --- /dev/null +++ b/code/api/src/Models/Static/AppConstants.cs @@ -0,0 +1,12 @@ +namespace IOL.GreatOffice.Api.Data.Static; + +public static class AppConstants +{ + public const string API_NAME = "Greatoffice API"; + public const string BASIC_AUTH_SCHEME = "BasicAuthenticationScheme"; + public const string TOKEN_ALLOW_READ = "TOKEN_ALLOW_READ"; + public const string TOKEN_ALLOW_CREATE = "TOKEN_ALLOW_CREATE"; + public const string TOKEN_ALLOW_UPDATE = "TOKEN_ALLOW_UPDATE"; + public const string TOKEN_ALLOW_DELETE = "TOKEN_ALLOW_DELETE"; + public const string VAULT_CACHE_KEY = "VAULT_CACHE_KEY"; +} diff --git a/code/api/src/Models/Static/AppCookies.cs b/code/api/src/Models/Static/AppCookies.cs new file mode 100644 index 0000000..e307b83 --- /dev/null +++ b/code/api/src/Models/Static/AppCookies.cs @@ -0,0 +1,7 @@ +namespace IOL.GreatOffice.Api.Data.Static; + +public static class AppCookies +{ + public const string Locale = "go_locale"; + public const string Session = "go_session"; +}
\ No newline at end of file diff --git a/code/api/src/Models/Static/AppDateTime.cs b/code/api/src/Models/Static/AppDateTime.cs new file mode 100644 index 0000000..880d2a8 --- /dev/null +++ b/code/api/src/Models/Static/AppDateTime.cs @@ -0,0 +1,16 @@ +namespace IOL.GreatOffice.Api.Data.Static; + +public static class AppDateTime +{ + private static DateTime? dateTime; + + public static DateTime UtcNow => dateTime ?? DateTime.UtcNow; + + public static void Set(DateTime setDateTime) { + dateTime = setDateTime; + } + + public static void Reset() { + dateTime = null; + } +} diff --git a/code/api/src/Models/Static/AppEnvironmentVariables.cs b/code/api/src/Models/Static/AppEnvironmentVariables.cs new file mode 100644 index 0000000..c3f821d --- /dev/null +++ b/code/api/src/Models/Static/AppEnvironmentVariables.cs @@ -0,0 +1,21 @@ +namespace IOL.GreatOffice.Api.Data.Static; + +public static class AppEnvironmentVariables +{ + /// <summary> + /// An access token that can be used to access the Hashicorp Vault instance that is available at VAULT_URL + /// </summary> + public const string VAULT_TOKEN = "VAULT_TOKEN"; + /// <summary> + /// An url pointing to the Hashicorp Vault instance the app should use + /// </summary> + public const string VAULT_URL = "VAULT_URL"; + /// <summary> + /// The duration of which to keep a local cached version of the configuration + /// </summary> + public const string VAULT_CACHE_TTL = "VAULT_CACHE_TTL"; + /// <summary> + /// The vault key name for the main configuration json object, described by <see cref="AppConfiguration"/> + /// </summary> + public const string MAIN_CONFIG_SHEET = "MAIN_CONFIG_SHEET"; +} diff --git a/code/api/src/Models/Static/AppHeaders.cs b/code/api/src/Models/Static/AppHeaders.cs new file mode 100644 index 0000000..d534aba --- /dev/null +++ b/code/api/src/Models/Static/AppHeaders.cs @@ -0,0 +1,7 @@ +namespace IOL.GreatOffice.Api.Data.Static; + +public static class AppHeaders +{ + public const string BROWSER_TIME_ZONE = "X-TimeZone"; + public const string IS_KNOWN_PROBLEM = "X-IsKnownProblem"; +} diff --git a/code/api/src/Models/Static/AppPaths.cs b/code/api/src/Models/Static/AppPaths.cs new file mode 100644 index 0000000..a24f5af --- /dev/null +++ b/code/api/src/Models/Static/AppPaths.cs @@ -0,0 +1,17 @@ + +namespace IOL.GreatOffice.Api.Data.Static; + +public static class AppPaths +{ + public static AppPath AppData => new() { + HostPath = Path.Combine(Directory.GetCurrentDirectory(), "AppData") + }; + + public static AppPath DataProtectionKeys => new() { + HostPath = Path.Combine(Directory.GetCurrentDirectory(), "AppData", "dp-keys") + }; + + public static AppPath Frontend => new() { + HostPath = Path.Combine(Directory.GetCurrentDirectory(), "Frontend") + }; +} diff --git a/code/api/src/Models/Static/JsonSettings.cs b/code/api/src/Models/Static/JsonSettings.cs new file mode 100644 index 0000000..a163c11 --- /dev/null +++ b/code/api/src/Models/Static/JsonSettings.cs @@ -0,0 +1,11 @@ +namespace IOL.GreatOffice.Api.Data.Static; + +public static class JsonSettings +{ + public static Action<JsonOptions> Default { get; } = options => { + options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles; + options.JsonSerializerOptions.PropertyNameCaseInsensitive = true; + options.JsonSerializerOptions.NumberHandling = JsonNumberHandling.AllowReadingFromString; + options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; + }; +} |
