From d3a4f66b0e7168f3ea387f4df32a91f055ded1e2 Mon Sep 17 00:00:00 2001 From: Jonathan Bourdon Date: Tue, 15 Apr 2025 02:56:24 -0400 Subject: [PATCH] Adds deletion to audits on Creator --- .../Contents/Data/ContentDbContext.cs | 25 +- .../src/Web/Features/Contents/Data/Creator.cs | 11 +- ...4846_AddsCreatorDeletionAudits.Designer.cs | 387 ++++++++++++++++++ ...0250415064846_AddsCreatorDeletionAudits.cs | 57 +++ ...AddsDeleteToContentQueryFilter.Designer.cs | 385 +++++++++++++++++ ...15065154_AddsDeleteToContentQueryFilter.cs | 47 +++ .../ContentDbContextModelSnapshot.cs | 15 +- .../Contents/Handlers/RemoveCreator.cs | 3 + 8 files changed, 920 insertions(+), 10 deletions(-) create mode 100644 backend/src/Web/Features/Contents/Data/Migrations/20250415064846_AddsCreatorDeletionAudits.Designer.cs create mode 100644 backend/src/Web/Features/Contents/Data/Migrations/20250415064846_AddsCreatorDeletionAudits.cs create mode 100644 backend/src/Web/Features/Contents/Data/Migrations/20250415065154_AddsDeleteToContentQueryFilter.Designer.cs create mode 100644 backend/src/Web/Features/Contents/Data/Migrations/20250415065154_AddsDeleteToContentQueryFilter.cs diff --git a/backend/src/Web/Features/Contents/Data/ContentDbContext.cs b/backend/src/Web/Features/Contents/Data/ContentDbContext.cs index 26e74a2..a752534 100644 --- a/backend/src/Web/Features/Contents/Data/ContentDbContext.cs +++ b/backend/src/Web/Features/Contents/Data/ContentDbContext.cs @@ -8,13 +8,14 @@ public class ContentDbContext( public DbSet Contents => Set(); public DbSet Creators => Set(); - public DbSet Slugs => Set(); + public DbSet Slugs => Set(); protected override void OnModelCreating( ModelBuilder modelBuilder) { modelBuilder.HasDefaultSchema(SchemaName); + modelBuilder .Entity() .Property(c => c.CreatedAt) @@ -25,8 +26,9 @@ public class ContentDbContext( .Entity() .HasOne(c => c.Creator) .WithMany() - .HasForeignKey(c => c.CreatedBy); - + .HasForeignKey(c => c.CreatedBy) + .IsRequired(false); + modelBuilder .Entity() .OwnsMany(c => c.Reactions) @@ -35,17 +37,24 @@ public class ContentDbContext( modelBuilder .Entity() .Property(c => c.ThumbnailUrl); - + + modelBuilder .Entity() .Property(x => x.NormalizedName) .HasComputedColumnSql("LOWER( \"Content\".\"Slugs\".\"Name\")", stored: true); - + modelBuilder .Entity() .HasIndex(x => x.NormalizedName) .IsUnique(); + + modelBuilder + .Entity() + .Property(c => c.IsDeleted) + .HasComputedColumnSql("\"DeletedAt\" IS NOT NULL", stored: true); // bool + modelBuilder .Entity() .OwnsOne(x => x.Socials) @@ -55,10 +64,14 @@ public class ContentDbContext( .Entity() .OwnsOne(x => x.Images) .ToTable(nameof(Images)); - + modelBuilder .Entity() .OwnsOne(x => x.PresentationInfos) .ToTable(nameof(PresentationInfos)); + + modelBuilder + .Entity() + .HasQueryFilter(c => !c.IsDeleted); } } diff --git a/backend/src/Web/Features/Contents/Data/Creator.cs b/backend/src/Web/Features/Contents/Data/Creator.cs index a33b7ec..fd29df8 100644 --- a/backend/src/Web/Features/Contents/Data/Creator.cs +++ b/backend/src/Web/Features/Contents/Data/Creator.cs @@ -5,8 +5,17 @@ namespace Hutopy.Web.Features.Contents.Data; public class Creator { public Guid Id { get; set; } + public Guid CreatedBy { get; set; } - public DateTimeOffset CreatedAt { get; init; } + public DateTimeOffset CreatedAt { get; init; } + public Guid? DeletedBy { get; set; } + public DateTimeOffset? DeletedAt { get; set; } + + /// + /// Soft‑delete flag (false by default, true once DeletedAt is set) + /// + public bool IsDeleted { get; private set; } // private set → EF updates it + public bool AcceptDonation { get; set; } public bool Verified { get; set; } public Slugs Slugs { get; set; } = null!; diff --git a/backend/src/Web/Features/Contents/Data/Migrations/20250415064846_AddsCreatorDeletionAudits.Designer.cs b/backend/src/Web/Features/Contents/Data/Migrations/20250415064846_AddsCreatorDeletionAudits.Designer.cs new file mode 100644 index 0000000..8d674cb --- /dev/null +++ b/backend/src/Web/Features/Contents/Data/Migrations/20250415064846_AddsCreatorDeletionAudits.Designer.cs @@ -0,0 +1,387 @@ +// +using System; +using Hutopy.Web.Features.Contents.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Hutopy.Web.Features.Contents.Data.Migrations +{ + [DbContext(typeof(ContentDbContext))] + [Migration("20250415064846_AddsCreatorDeletionAudits")] + partial class AddsCreatorDeletionAudits + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("Content") + .HasAnnotation("ProductVersion", "9.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Hutopy.Web.Features.Contents.Data.Content", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedBy") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("HtmlFileUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("ThumbnailUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.PrimitiveCollection("Urls") + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.ToTable("Contents", "Content"); + }); + + modelBuilder.Entity("Hutopy.Web.Features.Contents.Data.Creator", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AcceptDonation") + .HasColumnType("boolean"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedBy") + .HasColumnType("uuid"); + + b.Property("IsDeleted") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("boolean") + .HasComputedColumnSql("\"DeletedAt\" IS NOT NULL", true); + + b.Property("SlugsId") + .HasColumnType("uuid"); + + b.Property("Title") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Verified") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("SlugsId"); + + b.ToTable("Creators", "Content"); + }); + + modelBuilder.Entity("Hutopy.Web.Features.Contents.Data.Slugs", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("NormalizedName") + .IsRequired() + .ValueGeneratedOnAddOrUpdate() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasComputedColumnSql("LOWER( \"Content\".\"Slugs\".\"Name\")", true); + + b.Property("ReservedUntil") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique(); + + b.ToTable("Slugs", "Content"); + }); + + modelBuilder.Entity("Hutopy.Web.Features.Contents.Data.Content", b => + { + b.HasOne("Hutopy.Web.Features.Contents.Data.Creator", "Creator") + .WithMany() + .HasForeignKey("CreatedBy") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsMany("Hutopy.Web.Features.Contents.Data.ContentReaction", "Reactions", b1 => + { + b1.Property("ContentId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("Reaction") + .HasColumnType("integer"); + + b1.Property("UserId") + .HasColumnType("uuid"); + + b1.Property("UserName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b1.HasKey("ContentId", "Id"); + + b1.ToTable("Reactions", "Content"); + + b1.WithOwner() + .HasForeignKey("ContentId"); + }); + + b.Navigation("Creator"); + + b.Navigation("Reactions"); + }); + + modelBuilder.Entity("Hutopy.Web.Features.Contents.Data.Creator", b => + { + b.HasOne("Hutopy.Web.Features.Contents.Data.Slugs", "Slugs") + .WithMany() + .HasForeignKey("SlugsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Hutopy.Web.Features.Contents.Data.Images", "Images", b1 => + { + b1.Property("CreatorId") + .HasColumnType("uuid"); + + b1.Property("Banner") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.Property("Logo") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.HasKey("CreatorId"); + + b1.ToTable("Images", "Content"); + + b1.WithOwner() + .HasForeignKey("CreatorId"); + }); + + b.OwnsOne("Hutopy.Web.Features.Contents.Data.PresentationInfos", "PresentationInfos", b1 => + { + b1.Property("CreatorId") + .HasColumnType("uuid"); + + b1.Property("Email") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b1.Property("Image1Url") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.Property("Image2Url") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.Property("Image3Url") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.Property("Image4Url") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.Property("ImagesSubtitle") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b1.Property("ImagesText") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("character varying(10000)"); + + b1.Property("MainImageText") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("character varying(10000)"); + + b1.Property("MainImageUrl") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.Property("MainVideoText") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("character varying(10000)"); + + b1.Property("PhoneNumber") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b1.Property("Title") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b1.Property("VideoSubtitle") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b1.Property("VideoSubtitleMain") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b1.Property("VideoText") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("character varying(10000)"); + + b1.Property("VideoUrl") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.Property("VideoUrlMain") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.HasKey("CreatorId"); + + b1.ToTable("PresentationInfos", "Content"); + + b1.WithOwner() + .HasForeignKey("CreatorId"); + }); + + b.OwnsOne("Hutopy.Web.Features.Contents.Data.Socials", "Socials", b1 => + { + b1.Property("CreatorId") + .HasColumnType("uuid"); + + b1.Property("FacebookUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.Property("InstagramUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.Property("LinkedInUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.Property("RedditUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.Property("TikTokUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.Property("WebsiteUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.Property("XUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.Property("YoutubeUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.HasKey("CreatorId"); + + b1.ToTable("Socials", "Content"); + + b1.WithOwner() + .HasForeignKey("CreatorId"); + }); + + b.Navigation("Images") + .IsRequired(); + + b.Navigation("PresentationInfos") + .IsRequired(); + + b.Navigation("Slugs"); + + b.Navigation("Socials") + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Web/Features/Contents/Data/Migrations/20250415064846_AddsCreatorDeletionAudits.cs b/backend/src/Web/Features/Contents/Data/Migrations/20250415064846_AddsCreatorDeletionAudits.cs new file mode 100644 index 0000000..da22f29 --- /dev/null +++ b/backend/src/Web/Features/Contents/Data/Migrations/20250415064846_AddsCreatorDeletionAudits.cs @@ -0,0 +1,57 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Hutopy.Web.Features.Contents.Data.Migrations +{ + /// + public partial class AddsCreatorDeletionAudits : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "DeletedAt", + schema: "Content", + table: "Creators", + type: "timestamp with time zone", + nullable: true); + + migrationBuilder.AddColumn( + name: "DeletedBy", + schema: "Content", + table: "Creators", + type: "uuid", + nullable: true); + + migrationBuilder.AddColumn( + name: "IsDeleted", + schema: "Content", + table: "Creators", + type: "boolean", + nullable: false, + computedColumnSql: "\"DeletedAt\" IS NOT NULL", + stored: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "IsDeleted", + schema: "Content", + table: "Creators"); + + migrationBuilder.DropColumn( + name: "DeletedAt", + schema: "Content", + table: "Creators"); + + migrationBuilder.DropColumn( + name: "DeletedBy", + schema: "Content", + table: "Creators"); + } + } +} diff --git a/backend/src/Web/Features/Contents/Data/Migrations/20250415065154_AddsDeleteToContentQueryFilter.Designer.cs b/backend/src/Web/Features/Contents/Data/Migrations/20250415065154_AddsDeleteToContentQueryFilter.Designer.cs new file mode 100644 index 0000000..4cd27f4 --- /dev/null +++ b/backend/src/Web/Features/Contents/Data/Migrations/20250415065154_AddsDeleteToContentQueryFilter.Designer.cs @@ -0,0 +1,385 @@ +// +using System; +using Hutopy.Web.Features.Contents.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Hutopy.Web.Features.Contents.Data.Migrations +{ + [DbContext(typeof(ContentDbContext))] + [Migration("20250415065154_AddsDeleteToContentQueryFilter")] + partial class AddsDeleteToContentQueryFilter + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("Content") + .HasAnnotation("ProductVersion", "9.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Hutopy.Web.Features.Contents.Data.Content", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("CURRENT_TIMESTAMP"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedBy") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("HtmlFileUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("ThumbnailUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.PrimitiveCollection("Urls") + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.ToTable("Contents", "Content"); + }); + + modelBuilder.Entity("Hutopy.Web.Features.Contents.Data.Creator", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AcceptDonation") + .HasColumnType("boolean"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedBy") + .HasColumnType("uuid"); + + b.Property("IsDeleted") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("boolean") + .HasComputedColumnSql("\"DeletedAt\" IS NOT NULL", true); + + b.Property("SlugsId") + .HasColumnType("uuid"); + + b.Property("Title") + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b.Property("Verified") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("SlugsId"); + + b.ToTable("Creators", "Content"); + }); + + modelBuilder.Entity("Hutopy.Web.Features.Contents.Data.Slugs", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("NormalizedName") + .IsRequired() + .ValueGeneratedOnAddOrUpdate() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasComputedColumnSql("LOWER( \"Content\".\"Slugs\".\"Name\")", true); + + b.Property("ReservedUntil") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique(); + + b.ToTable("Slugs", "Content"); + }); + + modelBuilder.Entity("Hutopy.Web.Features.Contents.Data.Content", b => + { + b.HasOne("Hutopy.Web.Features.Contents.Data.Creator", "Creator") + .WithMany() + .HasForeignKey("CreatedBy"); + + b.OwnsMany("Hutopy.Web.Features.Contents.Data.ContentReaction", "Reactions", b1 => + { + b1.Property("ContentId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("Reaction") + .HasColumnType("integer"); + + b1.Property("UserId") + .HasColumnType("uuid"); + + b1.Property("UserName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b1.HasKey("ContentId", "Id"); + + b1.ToTable("Reactions", "Content"); + + b1.WithOwner() + .HasForeignKey("ContentId"); + }); + + b.Navigation("Creator"); + + b.Navigation("Reactions"); + }); + + modelBuilder.Entity("Hutopy.Web.Features.Contents.Data.Creator", b => + { + b.HasOne("Hutopy.Web.Features.Contents.Data.Slugs", "Slugs") + .WithMany() + .HasForeignKey("SlugsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Hutopy.Web.Features.Contents.Data.Images", "Images", b1 => + { + b1.Property("CreatorId") + .HasColumnType("uuid"); + + b1.Property("Banner") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.Property("Logo") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.HasKey("CreatorId"); + + b1.ToTable("Images", "Content"); + + b1.WithOwner() + .HasForeignKey("CreatorId"); + }); + + b.OwnsOne("Hutopy.Web.Features.Contents.Data.PresentationInfos", "PresentationInfos", b1 => + { + b1.Property("CreatorId") + .HasColumnType("uuid"); + + b1.Property("Email") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b1.Property("Image1Url") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.Property("Image2Url") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.Property("Image3Url") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.Property("Image4Url") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.Property("ImagesSubtitle") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b1.Property("ImagesText") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("character varying(10000)"); + + b1.Property("MainImageText") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("character varying(10000)"); + + b1.Property("MainImageUrl") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.Property("MainVideoText") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("character varying(10000)"); + + b1.Property("PhoneNumber") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)"); + + b1.Property("Title") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b1.Property("VideoSubtitle") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b1.Property("VideoSubtitleMain") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b1.Property("VideoText") + .IsRequired() + .HasMaxLength(10000) + .HasColumnType("character varying(10000)"); + + b1.Property("VideoUrl") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.Property("VideoUrlMain") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.HasKey("CreatorId"); + + b1.ToTable("PresentationInfos", "Content"); + + b1.WithOwner() + .HasForeignKey("CreatorId"); + }); + + b.OwnsOne("Hutopy.Web.Features.Contents.Data.Socials", "Socials", b1 => + { + b1.Property("CreatorId") + .HasColumnType("uuid"); + + b1.Property("FacebookUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.Property("InstagramUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.Property("LinkedInUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.Property("RedditUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.Property("TikTokUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.Property("WebsiteUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.Property("XUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.Property("YoutubeUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b1.HasKey("CreatorId"); + + b1.ToTable("Socials", "Content"); + + b1.WithOwner() + .HasForeignKey("CreatorId"); + }); + + b.Navigation("Images") + .IsRequired(); + + b.Navigation("PresentationInfos") + .IsRequired(); + + b.Navigation("Slugs"); + + b.Navigation("Socials") + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Web/Features/Contents/Data/Migrations/20250415065154_AddsDeleteToContentQueryFilter.cs b/backend/src/Web/Features/Contents/Data/Migrations/20250415065154_AddsDeleteToContentQueryFilter.cs new file mode 100644 index 0000000..1ea43a6 --- /dev/null +++ b/backend/src/Web/Features/Contents/Data/Migrations/20250415065154_AddsDeleteToContentQueryFilter.cs @@ -0,0 +1,47 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Hutopy.Web.Features.Contents.Data.Migrations +{ + /// + public partial class AddsDeleteToContentQueryFilter : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Contents_Creators_CreatedBy", + schema: "Content", + table: "Contents"); + + migrationBuilder.AddForeignKey( + name: "FK_Contents_Creators_CreatedBy", + schema: "Content", + table: "Contents", + column: "CreatedBy", + principalSchema: "Content", + principalTable: "Creators", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Contents_Creators_CreatedBy", + schema: "Content", + table: "Contents"); + + migrationBuilder.AddForeignKey( + name: "FK_Contents_Creators_CreatedBy", + schema: "Content", + table: "Contents", + column: "CreatedBy", + principalSchema: "Content", + principalTable: "Creators", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + } +} diff --git a/backend/src/Web/Features/Contents/Data/Migrations/ContentDbContextModelSnapshot.cs b/backend/src/Web/Features/Contents/Data/Migrations/ContentDbContextModelSnapshot.cs index 3152c58..c6adf85 100644 --- a/backend/src/Web/Features/Contents/Data/Migrations/ContentDbContextModelSnapshot.cs +++ b/backend/src/Web/Features/Contents/Data/Migrations/ContentDbContextModelSnapshot.cs @@ -86,6 +86,17 @@ namespace Hutopy.Web.Features.Contents.Data.Migrations b.Property("CreatedBy") .HasColumnType("uuid"); + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("DeletedBy") + .HasColumnType("uuid"); + + b.Property("IsDeleted") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("boolean") + .HasComputedColumnSql("\"DeletedAt\" IS NOT NULL", true); + b.Property("SlugsId") .HasColumnType("uuid"); @@ -145,9 +156,7 @@ namespace Hutopy.Web.Features.Contents.Data.Migrations { b.HasOne("Hutopy.Web.Features.Contents.Data.Creator", "Creator") .WithMany() - .HasForeignKey("CreatedBy") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); + .HasForeignKey("CreatedBy"); b.OwnsMany("Hutopy.Web.Features.Contents.Data.ContentReaction", "Reactions", b1 => { diff --git a/backend/src/Web/Features/Contents/Handlers/RemoveCreator.cs b/backend/src/Web/Features/Contents/Handlers/RemoveCreator.cs index a5020fc..b3f3030 100644 --- a/backend/src/Web/Features/Contents/Handlers/RemoveCreator.cs +++ b/backend/src/Web/Features/Contents/Handlers/RemoveCreator.cs @@ -50,6 +50,9 @@ public sealed class RemoveCreatorHandler( return; } + creator.DeletedAt = DateTimeOffset.UtcNow; + creator.DeletedBy = User.GetUserId(); + creator.Slugs.Active = false; await context.SaveChangesAsync(ct);