diff --git a/backend/src/Socialize.Api/Data/AppDbContext.cs b/backend/src/Socialize.Api/Data/AppDbContext.cs index c1088c4..e4fc389 100644 --- a/backend/src/Socialize.Api/Data/AppDbContext.cs +++ b/backend/src/Socialize.Api/Data/AppDbContext.cs @@ -32,195 +32,13 @@ public class AppDbContext( { base.OnModelCreating(modelBuilder); - modelBuilder.Entity(workspace => - { - workspace.ToTable("Workspaces"); - workspace.HasKey(x => x.Id); - workspace.Property(x => x.Name).HasMaxLength(256).IsRequired(); - workspace.Property(x => x.Slug).HasMaxLength(128).IsRequired(); - workspace.Property(x => x.TimeZone).HasMaxLength(128).IsRequired(); - workspace.Property(x => x.CreatedAt) - .ValueGeneratedOnAdd() - .HasDefaultValueSql("CURRENT_TIMESTAMP"); - workspace.HasIndex(x => x.Slug).IsUnique(); - workspace.HasIndex(x => x.OwnerUserId); - }); - - modelBuilder.Entity(workspaceInvite => - { - workspaceInvite.ToTable("WorkspaceInvites"); - workspaceInvite.HasKey(x => x.Id); - workspaceInvite.Property(x => x.Email).HasMaxLength(256).IsRequired(); - workspaceInvite.Property(x => x.Role).HasMaxLength(64).IsRequired(); - workspaceInvite.Property(x => x.Status).HasMaxLength(64).IsRequired(); - workspaceInvite.Property(x => x.CreatedAt) - .ValueGeneratedOnAdd() - .HasDefaultValueSql("CURRENT_TIMESTAMP"); - workspaceInvite.HasIndex(x => x.WorkspaceId); - workspaceInvite.HasIndex(x => new { x.WorkspaceId, x.Email, x.Status }); - }); - - modelBuilder.Entity(client => - { - client.ToTable("Clients"); - client.HasKey(x => x.Id); - client.Property(x => x.Name).HasMaxLength(256).IsRequired(); - client.Property(x => x.Status).HasMaxLength(64).IsRequired(); - client.Property(x => x.PortraitUrl).HasMaxLength(2048); - client.Property(x => x.PrimaryContactName).HasMaxLength(256); - client.Property(x => x.PrimaryContactEmail).HasMaxLength(256); - client.Property(x => x.PrimaryContactPortraitUrl).HasMaxLength(2048); - client.Property(x => x.CreatedAt) - .ValueGeneratedOnAdd() - .HasDefaultValueSql("CURRENT_TIMESTAMP"); - client.HasIndex(x => new { x.WorkspaceId, x.Name }).IsUnique(); - client.HasIndex(x => x.WorkspaceId); - }); - - modelBuilder.Entity(project => - { - project.ToTable("Projects"); - project.HasKey(x => x.Id); - project.Property(x => x.Name).HasMaxLength(256).IsRequired(); - project.Property(x => x.Description).HasMaxLength(4000); - project.Property(x => x.Notes).HasMaxLength(4000); - project.Property(x => x.Status).HasMaxLength(64).IsRequired(); - project.Property(x => x.CreatedAt) - .ValueGeneratedOnAdd() - .HasDefaultValueSql("CURRENT_TIMESTAMP"); - project.HasIndex(x => new { x.ClientId, x.Name }).IsUnique(); - project.HasIndex(x => x.WorkspaceId); - project.HasIndex(x => x.ClientId); - }); - - modelBuilder.Entity(contentItem => - { - contentItem.ToTable("ContentItems"); - contentItem.HasKey(x => x.Id); - contentItem.Property(x => x.Title).HasMaxLength(256).IsRequired(); - contentItem.Property(x => x.PublicationMessage).HasMaxLength(4000).IsRequired(); - contentItem.Property(x => x.PublicationTargets).HasMaxLength(512).IsRequired(); - contentItem.Property(x => x.Hashtags).HasMaxLength(1024); - contentItem.Property(x => x.Status).HasMaxLength(64).IsRequired(); - contentItem.Property(x => x.CurrentRevisionLabel).HasMaxLength(32).IsRequired(); - contentItem.Property(x => x.CreatedAt) - .ValueGeneratedOnAdd() - .HasDefaultValueSql("CURRENT_TIMESTAMP"); - contentItem.HasIndex(x => x.WorkspaceId); - contentItem.HasIndex(x => x.ClientId); - contentItem.HasIndex(x => x.ProjectId); - }); - - modelBuilder.Entity(revision => - { - revision.ToTable("ContentItemRevisions"); - revision.HasKey(x => x.Id); - revision.Property(x => x.RevisionLabel).HasMaxLength(32).IsRequired(); - revision.Property(x => x.Title).HasMaxLength(256).IsRequired(); - revision.Property(x => x.PublicationMessage).HasMaxLength(4000).IsRequired(); - revision.Property(x => x.PublicationTargets).HasMaxLength(512).IsRequired(); - revision.Property(x => x.Hashtags).HasMaxLength(1024); - revision.Property(x => x.ChangeSummary).HasMaxLength(1024); - revision.Property(x => x.CreatedAt) - .ValueGeneratedOnAdd() - .HasDefaultValueSql("CURRENT_TIMESTAMP"); - revision.HasIndex(x => x.ContentItemId); - revision.HasIndex(x => new { x.ContentItemId, x.RevisionNumber }).IsUnique(); - }); - - modelBuilder.Entity(asset => - { - asset.ToTable("Assets"); - asset.HasKey(x => x.Id); - asset.Property(x => x.AssetType).HasMaxLength(64).IsRequired(); - asset.Property(x => x.SourceType).HasMaxLength(64).IsRequired(); - asset.Property(x => x.DisplayName).HasMaxLength(256).IsRequired(); - asset.Property(x => x.GoogleDriveFileId).HasMaxLength(256); - asset.Property(x => x.GoogleDriveLink).HasMaxLength(2048); - asset.Property(x => x.PreviewUrl).HasMaxLength(2048); - asset.Property(x => x.CreatedAt) - .ValueGeneratedOnAdd() - .HasDefaultValueSql("CURRENT_TIMESTAMP"); - asset.HasIndex(x => x.WorkspaceId); - asset.HasIndex(x => x.ContentItemId); - }); - - modelBuilder.Entity(revision => - { - revision.ToTable("AssetRevisions"); - revision.HasKey(x => x.Id); - revision.Property(x => x.SourceReference).HasMaxLength(2048).IsRequired(); - revision.Property(x => x.PreviewUrl).HasMaxLength(2048); - revision.Property(x => x.Notes).HasMaxLength(1024); - revision.Property(x => x.CreatedAt) - .ValueGeneratedOnAdd() - .HasDefaultValueSql("CURRENT_TIMESTAMP"); - revision.HasIndex(x => x.AssetId); - revision.HasIndex(x => new { x.AssetId, x.RevisionNumber }).IsUnique(); - }); - - modelBuilder.Entity(comment => - { - comment.ToTable("Comments"); - comment.HasKey(x => x.Id); - comment.Property(x => x.AuthorDisplayName).HasMaxLength(256).IsRequired(); - comment.Property(x => x.AuthorEmail).HasMaxLength(256).IsRequired(); - comment.Property(x => x.Body).HasMaxLength(4000).IsRequired(); - comment.Property(x => x.CreatedAt) - .ValueGeneratedOnAdd() - .HasDefaultValueSql("CURRENT_TIMESTAMP"); - comment.HasIndex(x => x.WorkspaceId); - comment.HasIndex(x => x.ContentItemId); - comment.HasIndex(x => x.ParentCommentId); - }); - - modelBuilder.Entity(approvalRequest => - { - approvalRequest.ToTable("ApprovalRequests"); - approvalRequest.HasKey(x => x.Id); - approvalRequest.Property(x => x.Stage).HasMaxLength(64).IsRequired(); - approvalRequest.Property(x => x.ReviewerName).HasMaxLength(256).IsRequired(); - approvalRequest.Property(x => x.ReviewerEmail).HasMaxLength(256).IsRequired(); - approvalRequest.Property(x => x.State).HasMaxLength(64).IsRequired(); - approvalRequest.Property(x => x.AccessToken).HasMaxLength(64).IsRequired(); - approvalRequest.Property(x => x.SentAt) - .ValueGeneratedOnAdd() - .HasDefaultValueSql("CURRENT_TIMESTAMP"); - approvalRequest.HasIndex(x => x.WorkspaceId); - approvalRequest.HasIndex(x => x.ContentItemId); - approvalRequest.HasIndex(x => x.ReviewerEmail); - }); - - modelBuilder.Entity(approvalDecision => - { - approvalDecision.ToTable("ApprovalDecisions"); - approvalDecision.HasKey(x => x.Id); - approvalDecision.Property(x => x.Decision).HasMaxLength(64).IsRequired(); - approvalDecision.Property(x => x.Comment).HasMaxLength(2048); - approvalDecision.Property(x => x.DecidedByName).HasMaxLength(256).IsRequired(); - approvalDecision.Property(x => x.DecidedByEmail).HasMaxLength(256).IsRequired(); - approvalDecision.Property(x => x.CreatedAt) - .ValueGeneratedOnAdd() - .HasDefaultValueSql("CURRENT_TIMESTAMP"); - approvalDecision.HasIndex(x => x.ApprovalRequestId); - }); - - modelBuilder.Entity(notificationEvent => - { - notificationEvent.ToTable("NotificationEvents"); - notificationEvent.HasKey(x => x.Id); - notificationEvent.Property(x => x.EventType).HasMaxLength(128).IsRequired(); - notificationEvent.Property(x => x.EntityType).HasMaxLength(128).IsRequired(); - notificationEvent.Property(x => x.Message).HasMaxLength(1024).IsRequired(); - notificationEvent.Property(x => x.RecipientEmail).HasMaxLength(256); - notificationEvent.Property(x => x.MetadataJson).HasMaxLength(4000); - notificationEvent.Property(x => x.CreatedAt) - .ValueGeneratedOnAdd() - .HasDefaultValueSql("CURRENT_TIMESTAMP"); - notificationEvent.HasIndex(x => x.WorkspaceId); - notificationEvent.HasIndex(x => x.ContentItemId); - notificationEvent.HasIndex(x => x.RecipientUserId); - notificationEvent.HasIndex(x => x.CreatedAt); - }); + modelBuilder.ConfigureWorkspacesModule(); + modelBuilder.ConfigureClientsModule(); + modelBuilder.ConfigureProjectsModule(); + modelBuilder.ConfigureContentItemsModule(); + modelBuilder.ConfigureAssetsModule(); + modelBuilder.ConfigureCommentsModule(); + modelBuilder.ConfigureApprovalsModule(); + modelBuilder.ConfigureNotificationsModule(); } } diff --git a/backend/src/Socialize.Api/Modules/Approvals/Data/ApprovalModelConfiguration.cs b/backend/src/Socialize.Api/Modules/Approvals/Data/ApprovalModelConfiguration.cs new file mode 100644 index 0000000..9120d1a --- /dev/null +++ b/backend/src/Socialize.Api/Modules/Approvals/Data/ApprovalModelConfiguration.cs @@ -0,0 +1,40 @@ +namespace Socialize.Modules.Approvals.Data; + +public static class ApprovalModelConfiguration +{ + public static ModelBuilder ConfigureApprovalsModule(this ModelBuilder modelBuilder) + { + modelBuilder.Entity(approvalRequest => + { + approvalRequest.ToTable("ApprovalRequests"); + approvalRequest.HasKey(x => x.Id); + approvalRequest.Property(x => x.Stage).HasMaxLength(64).IsRequired(); + approvalRequest.Property(x => x.ReviewerName).HasMaxLength(256).IsRequired(); + approvalRequest.Property(x => x.ReviewerEmail).HasMaxLength(256).IsRequired(); + approvalRequest.Property(x => x.State).HasMaxLength(64).IsRequired(); + approvalRequest.Property(x => x.AccessToken).HasMaxLength(64).IsRequired(); + approvalRequest.Property(x => x.SentAt) + .ValueGeneratedOnAdd() + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + approvalRequest.HasIndex(x => x.WorkspaceId); + approvalRequest.HasIndex(x => x.ContentItemId); + approvalRequest.HasIndex(x => x.ReviewerEmail); + }); + + modelBuilder.Entity(approvalDecision => + { + approvalDecision.ToTable("ApprovalDecisions"); + approvalDecision.HasKey(x => x.Id); + approvalDecision.Property(x => x.Decision).HasMaxLength(64).IsRequired(); + approvalDecision.Property(x => x.Comment).HasMaxLength(2048); + approvalDecision.Property(x => x.DecidedByName).HasMaxLength(256).IsRequired(); + approvalDecision.Property(x => x.DecidedByEmail).HasMaxLength(256).IsRequired(); + approvalDecision.Property(x => x.CreatedAt) + .ValueGeneratedOnAdd() + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + approvalDecision.HasIndex(x => x.ApprovalRequestId); + }); + + return modelBuilder; + } +} diff --git a/backend/src/Socialize.Api/Modules/Assets/Data/AssetModelConfiguration.cs b/backend/src/Socialize.Api/Modules/Assets/Data/AssetModelConfiguration.cs new file mode 100644 index 0000000..9be2638 --- /dev/null +++ b/backend/src/Socialize.Api/Modules/Assets/Data/AssetModelConfiguration.cs @@ -0,0 +1,40 @@ +namespace Socialize.Modules.Assets.Data; + +public static class AssetModelConfiguration +{ + public static ModelBuilder ConfigureAssetsModule(this ModelBuilder modelBuilder) + { + modelBuilder.Entity(asset => + { + asset.ToTable("Assets"); + asset.HasKey(x => x.Id); + asset.Property(x => x.AssetType).HasMaxLength(64).IsRequired(); + asset.Property(x => x.SourceType).HasMaxLength(64).IsRequired(); + asset.Property(x => x.DisplayName).HasMaxLength(256).IsRequired(); + asset.Property(x => x.GoogleDriveFileId).HasMaxLength(256); + asset.Property(x => x.GoogleDriveLink).HasMaxLength(2048); + asset.Property(x => x.PreviewUrl).HasMaxLength(2048); + asset.Property(x => x.CreatedAt) + .ValueGeneratedOnAdd() + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + asset.HasIndex(x => x.WorkspaceId); + asset.HasIndex(x => x.ContentItemId); + }); + + modelBuilder.Entity(revision => + { + revision.ToTable("AssetRevisions"); + revision.HasKey(x => x.Id); + revision.Property(x => x.SourceReference).HasMaxLength(2048).IsRequired(); + revision.Property(x => x.PreviewUrl).HasMaxLength(2048); + revision.Property(x => x.Notes).HasMaxLength(1024); + revision.Property(x => x.CreatedAt) + .ValueGeneratedOnAdd() + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + revision.HasIndex(x => x.AssetId); + revision.HasIndex(x => new { x.AssetId, x.RevisionNumber }).IsUnique(); + }); + + return modelBuilder; + } +} diff --git a/backend/src/Socialize.Api/Modules/Clients/Data/ClientModelConfiguration.cs b/backend/src/Socialize.Api/Modules/Clients/Data/ClientModelConfiguration.cs new file mode 100644 index 0000000..a1082e5 --- /dev/null +++ b/backend/src/Socialize.Api/Modules/Clients/Data/ClientModelConfiguration.cs @@ -0,0 +1,26 @@ +namespace Socialize.Modules.Clients.Data; + +public static class ClientModelConfiguration +{ + public static ModelBuilder ConfigureClientsModule(this ModelBuilder modelBuilder) + { + modelBuilder.Entity(client => + { + client.ToTable("Clients"); + client.HasKey(x => x.Id); + client.Property(x => x.Name).HasMaxLength(256).IsRequired(); + client.Property(x => x.Status).HasMaxLength(64).IsRequired(); + client.Property(x => x.PortraitUrl).HasMaxLength(2048); + client.Property(x => x.PrimaryContactName).HasMaxLength(256); + client.Property(x => x.PrimaryContactEmail).HasMaxLength(256); + client.Property(x => x.PrimaryContactPortraitUrl).HasMaxLength(2048); + client.Property(x => x.CreatedAt) + .ValueGeneratedOnAdd() + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + client.HasIndex(x => new { x.WorkspaceId, x.Name }).IsUnique(); + client.HasIndex(x => x.WorkspaceId); + }); + + return modelBuilder; + } +} diff --git a/backend/src/Socialize.Api/Modules/Comments/Data/CommentModelConfiguration.cs b/backend/src/Socialize.Api/Modules/Comments/Data/CommentModelConfiguration.cs new file mode 100644 index 0000000..4970daa --- /dev/null +++ b/backend/src/Socialize.Api/Modules/Comments/Data/CommentModelConfiguration.cs @@ -0,0 +1,24 @@ +namespace Socialize.Modules.Comments.Data; + +public static class CommentModelConfiguration +{ + public static ModelBuilder ConfigureCommentsModule(this ModelBuilder modelBuilder) + { + modelBuilder.Entity(comment => + { + comment.ToTable("Comments"); + comment.HasKey(x => x.Id); + comment.Property(x => x.AuthorDisplayName).HasMaxLength(256).IsRequired(); + comment.Property(x => x.AuthorEmail).HasMaxLength(256).IsRequired(); + comment.Property(x => x.Body).HasMaxLength(4000).IsRequired(); + comment.Property(x => x.CreatedAt) + .ValueGeneratedOnAdd() + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + comment.HasIndex(x => x.WorkspaceId); + comment.HasIndex(x => x.ContentItemId); + comment.HasIndex(x => x.ParentCommentId); + }); + + return modelBuilder; + } +} diff --git a/backend/src/Socialize.Api/Modules/ContentItems/Data/ContentItemModelConfiguration.cs b/backend/src/Socialize.Api/Modules/ContentItems/Data/ContentItemModelConfiguration.cs new file mode 100644 index 0000000..087ac90 --- /dev/null +++ b/backend/src/Socialize.Api/Modules/ContentItems/Data/ContentItemModelConfiguration.cs @@ -0,0 +1,44 @@ +namespace Socialize.Modules.ContentItems.Data; + +public static class ContentItemModelConfiguration +{ + public static ModelBuilder ConfigureContentItemsModule(this ModelBuilder modelBuilder) + { + modelBuilder.Entity(contentItem => + { + contentItem.ToTable("ContentItems"); + contentItem.HasKey(x => x.Id); + contentItem.Property(x => x.Title).HasMaxLength(256).IsRequired(); + contentItem.Property(x => x.PublicationMessage).HasMaxLength(4000).IsRequired(); + contentItem.Property(x => x.PublicationTargets).HasMaxLength(512).IsRequired(); + contentItem.Property(x => x.Hashtags).HasMaxLength(1024); + contentItem.Property(x => x.Status).HasMaxLength(64).IsRequired(); + contentItem.Property(x => x.CurrentRevisionLabel).HasMaxLength(32).IsRequired(); + contentItem.Property(x => x.CreatedAt) + .ValueGeneratedOnAdd() + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + contentItem.HasIndex(x => x.WorkspaceId); + contentItem.HasIndex(x => x.ClientId); + contentItem.HasIndex(x => x.ProjectId); + }); + + modelBuilder.Entity(revision => + { + revision.ToTable("ContentItemRevisions"); + revision.HasKey(x => x.Id); + revision.Property(x => x.RevisionLabel).HasMaxLength(32).IsRequired(); + revision.Property(x => x.Title).HasMaxLength(256).IsRequired(); + revision.Property(x => x.PublicationMessage).HasMaxLength(4000).IsRequired(); + revision.Property(x => x.PublicationTargets).HasMaxLength(512).IsRequired(); + revision.Property(x => x.Hashtags).HasMaxLength(1024); + revision.Property(x => x.ChangeSummary).HasMaxLength(1024); + revision.Property(x => x.CreatedAt) + .ValueGeneratedOnAdd() + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + revision.HasIndex(x => x.ContentItemId); + revision.HasIndex(x => new { x.ContentItemId, x.RevisionNumber }).IsUnique(); + }); + + return modelBuilder; + } +} diff --git a/backend/src/Socialize.Api/Modules/Notifications/Data/NotificationModelConfiguration.cs b/backend/src/Socialize.Api/Modules/Notifications/Data/NotificationModelConfiguration.cs new file mode 100644 index 0000000..9c4c1e1 --- /dev/null +++ b/backend/src/Socialize.Api/Modules/Notifications/Data/NotificationModelConfiguration.cs @@ -0,0 +1,27 @@ +namespace Socialize.Modules.Notifications.Data; + +public static class NotificationModelConfiguration +{ + public static ModelBuilder ConfigureNotificationsModule(this ModelBuilder modelBuilder) + { + modelBuilder.Entity(notificationEvent => + { + notificationEvent.ToTable("NotificationEvents"); + notificationEvent.HasKey(x => x.Id); + notificationEvent.Property(x => x.EventType).HasMaxLength(128).IsRequired(); + notificationEvent.Property(x => x.EntityType).HasMaxLength(128).IsRequired(); + notificationEvent.Property(x => x.Message).HasMaxLength(1024).IsRequired(); + notificationEvent.Property(x => x.RecipientEmail).HasMaxLength(256); + notificationEvent.Property(x => x.MetadataJson).HasMaxLength(4000); + notificationEvent.Property(x => x.CreatedAt) + .ValueGeneratedOnAdd() + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + notificationEvent.HasIndex(x => x.WorkspaceId); + notificationEvent.HasIndex(x => x.ContentItemId); + notificationEvent.HasIndex(x => x.RecipientUserId); + notificationEvent.HasIndex(x => x.CreatedAt); + }); + + return modelBuilder; + } +} diff --git a/backend/src/Socialize.Api/Modules/Projects/Data/ProjectModelConfiguration.cs b/backend/src/Socialize.Api/Modules/Projects/Data/ProjectModelConfiguration.cs new file mode 100644 index 0000000..0f6fd1d --- /dev/null +++ b/backend/src/Socialize.Api/Modules/Projects/Data/ProjectModelConfiguration.cs @@ -0,0 +1,25 @@ +namespace Socialize.Modules.Projects.Data; + +public static class ProjectModelConfiguration +{ + public static ModelBuilder ConfigureProjectsModule(this ModelBuilder modelBuilder) + { + modelBuilder.Entity(project => + { + project.ToTable("Projects"); + project.HasKey(x => x.Id); + project.Property(x => x.Name).HasMaxLength(256).IsRequired(); + project.Property(x => x.Description).HasMaxLength(4000); + project.Property(x => x.Notes).HasMaxLength(4000); + project.Property(x => x.Status).HasMaxLength(64).IsRequired(); + project.Property(x => x.CreatedAt) + .ValueGeneratedOnAdd() + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + project.HasIndex(x => new { x.ClientId, x.Name }).IsUnique(); + project.HasIndex(x => x.WorkspaceId); + project.HasIndex(x => x.ClientId); + }); + + return modelBuilder; + } +} diff --git a/backend/src/Socialize.Api/Modules/Workspaces/Data/WorkspaceModelConfiguration.cs b/backend/src/Socialize.Api/Modules/Workspaces/Data/WorkspaceModelConfiguration.cs new file mode 100644 index 0000000..f2582f6 --- /dev/null +++ b/backend/src/Socialize.Api/Modules/Workspaces/Data/WorkspaceModelConfiguration.cs @@ -0,0 +1,37 @@ +namespace Socialize.Modules.Workspaces.Data; + +public static class WorkspaceModelConfiguration +{ + public static ModelBuilder ConfigureWorkspacesModule(this ModelBuilder modelBuilder) + { + modelBuilder.Entity(workspace => + { + workspace.ToTable("Workspaces"); + workspace.HasKey(x => x.Id); + workspace.Property(x => x.Name).HasMaxLength(256).IsRequired(); + workspace.Property(x => x.Slug).HasMaxLength(128).IsRequired(); + workspace.Property(x => x.TimeZone).HasMaxLength(128).IsRequired(); + workspace.Property(x => x.CreatedAt) + .ValueGeneratedOnAdd() + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + workspace.HasIndex(x => x.Slug).IsUnique(); + workspace.HasIndex(x => x.OwnerUserId); + }); + + modelBuilder.Entity(workspaceInvite => + { + workspaceInvite.ToTable("WorkspaceInvites"); + workspaceInvite.HasKey(x => x.Id); + workspaceInvite.Property(x => x.Email).HasMaxLength(256).IsRequired(); + workspaceInvite.Property(x => x.Role).HasMaxLength(64).IsRequired(); + workspaceInvite.Property(x => x.Status).HasMaxLength(64).IsRequired(); + workspaceInvite.Property(x => x.CreatedAt) + .ValueGeneratedOnAdd() + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + workspaceInvite.HasIndex(x => x.WorkspaceId); + workspaceInvite.HasIndex(x => new { x.WorkspaceId, x.Email, x.Status }); + }); + + return modelBuilder; + } +} diff --git a/docs/TASKS/platform-scaffold/002-contain-backend-feature-mapping.md b/docs/TASKS/platform-scaffold/002-contain-backend-feature-mapping.md new file mode 100644 index 0000000..2566dad --- /dev/null +++ b/docs/TASKS/platform-scaffold/002-contain-backend-feature-mapping.md @@ -0,0 +1,37 @@ +# Task: Contain backend feature mapping + +## Feature + +`docs/FEATURES/platform-scaffold.md` + +## Goal + +Move backend feature-specific persistence mapping out of the shared `AppDbContext` body and into the owning `Modules//Data` folders. + +## Context + +Architecture docs state that current backend feature code stays under `Modules/`. `AppDbContext` remains the shared EF Core composition point, but feature-owned model configuration should live with the feature entities. + +## Files Likely To Change + +- `backend/src/Socialize.Api/Data/AppDbContext.cs` +- `backend/src/Socialize.Api/Modules/*/Data/*` + +## Constraints + +- Do not change API contracts. +- Do not change table names, indexes, or column constraints. +- Do not introduce a broader persistence refactor. + +## Done When + +- [x] Feature entity mappings live under the owning module folders. +- [x] `AppDbContext` delegates feature configuration to modules. +- [x] Backend build passes. + +## Validation Commands + +```bash +dotnet build backend/Socialize.slnx +dotnet test backend/Socialize.slnx +```