feat(backend): add Artist entity and track ownership
Track.Artist was a plain string snapshot with no identity, and UserHasTrack doubled as both "in my library" and "I own this", conflating collection membership with administrative ownership. Introduce Artist/TrackArtist to give artist credits their own identity (needed so YouTube tracks with multiple credited artists, and future renames, can be modeled), and Track.OwnerUserId to represent ownership explicitly (local tracks only, enforced by a check constraint). CreateTrack now provisions the uploader's Artist row and links it via TrackArtist; DeleteTrack authorizes against OwnerUserId instead of any UserHasTrack row.
This commit is contained in:
+826
@@ -0,0 +1,826 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using Musify.Infrastructure.Persistence;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Musify.Infrastructure.Persistence.Migrations
|
||||
{
|
||||
[DbContext(typeof(Database))]
|
||||
[Migration("20260716214752_AddArtistsAndTrackOwnership")]
|
||||
partial class AddArtistsAndTrackOwnership
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "10.0.8")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("MassTransit.EntityFrameworkCoreIntegration.InboxState", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||
|
||||
b.Property<DateTime?>("Consumed")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<Guid>("ConsumerId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime?>("Delivered")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime?>("ExpirationTime")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<long?>("LastSequenceNumber")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<Guid>("LockId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("MessageId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("ReceiveCount")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("Received")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<byte[]>("RowVersion")
|
||||
.IsConcurrencyToken()
|
||||
.ValueGeneratedOnAddOrUpdate()
|
||||
.HasColumnType("bytea");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Delivered");
|
||||
|
||||
b.ToTable("InboxState");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MassTransit.EntityFrameworkCoreIntegration.OutboxMessage", b =>
|
||||
{
|
||||
b.Property<long>("SequenceNumber")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("bigint");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("SequenceNumber"));
|
||||
|
||||
b.Property<string>("Body")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ContentType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<Guid?>("ConversationId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid?>("CorrelationId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("DestinationAddress")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<DateTime?>("EnqueueTime")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime?>("ExpirationTime")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("FaultAddress")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("Headers")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid?>("InboxConsumerId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid?>("InboxMessageId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid?>("InitiatorId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("MessageId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("MessageType")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid?>("OutboxId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Properties")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid?>("RequestId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("ResponseAddress")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<DateTime>("SentTime")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("SourceAddress")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.HasKey("SequenceNumber");
|
||||
|
||||
b.HasIndex("EnqueueTime");
|
||||
|
||||
b.HasIndex("ExpirationTime");
|
||||
|
||||
b.HasIndex("OutboxId", "SequenceNumber")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("InboxMessageId", "InboxConsumerId", "SequenceNumber")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("OutboxMessage");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MassTransit.EntityFrameworkCoreIntegration.OutboxState", b =>
|
||||
{
|
||||
b.Property<Guid>("OutboxId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("Created")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<DateTime?>("Delivered")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<long?>("LastSequenceNumber")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<Guid>("LockId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<byte[]>("RowVersion")
|
||||
.IsConcurrencyToken()
|
||||
.ValueGeneratedOnAddOrUpdate()
|
||||
.HasColumnType("bytea");
|
||||
|
||||
b.HasKey("OutboxId");
|
||||
|
||||
b.HasIndex("Created");
|
||||
|
||||
b.ToTable("OutboxState");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Musify.Domain.Entities.Artist", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("ExternalId")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<long?>("UserId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ExternalId")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasFilter("\"ExternalId\" IS NULL AND \"UserId\" IS NULL");
|
||||
|
||||
b.HasIndex("UserId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Artists");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Musify.Domain.Entities.ListeningHistory", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("ListenedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<Guid>("TrackId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<long>("UserId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TrackId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("ListeningHistory");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Musify.Domain.Entities.PlayList", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.IsRequired()
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("character varying(256)");
|
||||
|
||||
b.Property<string>("LargePictureName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<int>("LifeCycleStatus")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("MediumPictureName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)");
|
||||
|
||||
b.Property<string>("OriginalPictureName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("SmallPictureName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<long>("UserId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("PlayLists");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Musify.Domain.Entities.PlayListHasTrack", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<DateTime>("AddedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<Guid>("PlayListId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("Position")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<Guid>("TrackId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("PlayListId");
|
||||
|
||||
b.HasIndex("TrackId");
|
||||
|
||||
b.ToTable("PlayListHasTrack");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Musify.Domain.Entities.Track", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Artist")
|
||||
.HasMaxLength(200)
|
||||
.HasColumnType("character varying(200)");
|
||||
|
||||
b.Property<string>("AudioFolderName")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<int>("AudioTranscodeProcessingStatus")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<bool>("DownloadRequested")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<int>("Duration")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("ExternalId")
|
||||
.HasMaxLength(16)
|
||||
.HasColumnType("character varying(16)");
|
||||
|
||||
b.Property<string>("LargePictureName")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<DateTime?>("LastRetryAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int>("LifeCycleStatus")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("MediumPictureName")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("NormalizedTitle")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)");
|
||||
|
||||
b.Property<string>("OriginalAudioName")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("OriginalPictureName")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<long?>("OwnerUserId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int>("PicturesProcessingStatus")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("RetryCount")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("SmallPictureName")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<int>("Source")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Title")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("character varying(50)");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("OwnerUserId");
|
||||
|
||||
b.HasIndex("Source", "ExternalId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Tracks", t =>
|
||||
{
|
||||
t.HasCheckConstraint("CK_Tracks_OwnerUserId_Source", "(\"OwnerUserId\" IS NOT NULL) = (\"Source\" = 0)");
|
||||
});
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Musify.Domain.Entities.TrackArtist", b =>
|
||||
{
|
||||
b.Property<Guid>("TrackId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("ArtistId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("Position")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("TrackId", "ArtistId");
|
||||
|
||||
b.HasIndex("ArtistId");
|
||||
|
||||
b.ToTable("TrackArtist");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Musify.Domain.Entities.Upload", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Bucket")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ContentType")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("CreatedTime")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int>("State")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Upload");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Musify.Domain.Entities.UploadIntent", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Bucket")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)");
|
||||
|
||||
b.Property<string>("ContentType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<long?>("ExpectedSizeBytes")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTime>("ExpiresAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasMaxLength(512)
|
||||
.HasColumnType("character varying(512)");
|
||||
|
||||
b.Property<string>("ObjectName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(128)
|
||||
.HasColumnType("character varying(128)");
|
||||
|
||||
b.Property<int>("Purpose")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<long>("UserId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("UploadIntents");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Musify.Domain.Entities.User", b =>
|
||||
{
|
||||
b.Property<long>("Id")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("FirstName")
|
||||
.HasMaxLength(48)
|
||||
.HasColumnType("character varying(48)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(48)
|
||||
.HasColumnType("character varying(48)");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(48)
|
||||
.HasColumnType("character varying(48)");
|
||||
|
||||
b.Property<string>("ProfilePictureUrl")
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("character varying(2048)");
|
||||
|
||||
b.Property<string>("SecondName")
|
||||
.HasMaxLength(48)
|
||||
.HasColumnType("character varying(48)");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Musify.Domain.Entities.UserHasTrack", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("TrackId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<long>("UserId")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("TrackId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("UserHasTrack");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Musify.Infrastructure.MassTransit.Sagas.PlayListProcessingState", b =>
|
||||
{
|
||||
b.Property<Guid>("CorrelationId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Bucket")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("CurrentState")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("PictureKey")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("CorrelationId");
|
||||
|
||||
b.ToTable("PlayListProcessingState");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Musify.Infrastructure.MassTransit.Sagas.TrackProcessingState", b =>
|
||||
{
|
||||
b.Property<Guid>("CorrelationId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("AudioKey")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<bool>("AudioProcessed")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("Bucket")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<string>("CurrentState")
|
||||
.IsRequired()
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("character varying(64)");
|
||||
|
||||
b.Property<string>("PictureKey")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<bool>("PictureProcessed")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("CorrelationId");
|
||||
|
||||
b.ToTable("TrackProcessingState");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MassTransit.EntityFrameworkCoreIntegration.OutboxMessage", b =>
|
||||
{
|
||||
b.HasOne("MassTransit.EntityFrameworkCoreIntegration.OutboxState", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("OutboxId");
|
||||
|
||||
b.HasOne("MassTransit.EntityFrameworkCoreIntegration.InboxState", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("InboxMessageId", "InboxConsumerId")
|
||||
.HasPrincipalKey("MessageId", "ConsumerId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Musify.Domain.Entities.Artist", b =>
|
||||
{
|
||||
b.HasOne("Musify.Domain.Entities.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Musify.Domain.Entities.ListeningHistory", b =>
|
||||
{
|
||||
b.HasOne("Musify.Domain.Entities.Track", "Track")
|
||||
.WithMany("ListeningHistories")
|
||||
.HasForeignKey("TrackId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Musify.Domain.Entities.User", "User")
|
||||
.WithMany("ListeningHistories")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Track");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Musify.Domain.Entities.PlayList", b =>
|
||||
{
|
||||
b.HasOne("Musify.Domain.Entities.User", "User")
|
||||
.WithMany("PlayLists")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Musify.Domain.Entities.PlayListHasTrack", b =>
|
||||
{
|
||||
b.HasOne("Musify.Domain.Entities.PlayList", "PlayList")
|
||||
.WithMany("PlayListTracks")
|
||||
.HasForeignKey("PlayListId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Musify.Domain.Entities.Track", "Track")
|
||||
.WithMany("PlayListTracks")
|
||||
.HasForeignKey("TrackId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("PlayList");
|
||||
|
||||
b.Navigation("Track");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Musify.Domain.Entities.Track", b =>
|
||||
{
|
||||
b.HasOne("Musify.Domain.Entities.User", "Owner")
|
||||
.WithMany()
|
||||
.HasForeignKey("OwnerUserId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
b.Navigation("Owner");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Musify.Domain.Entities.TrackArtist", b =>
|
||||
{
|
||||
b.HasOne("Musify.Domain.Entities.Artist", "Artist")
|
||||
.WithMany("TrackArtists")
|
||||
.HasForeignKey("ArtistId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Musify.Domain.Entities.Track", "Track")
|
||||
.WithMany("TrackArtists")
|
||||
.HasForeignKey("TrackId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Artist");
|
||||
|
||||
b.Navigation("Track");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Musify.Domain.Entities.UploadIntent", b =>
|
||||
{
|
||||
b.HasOne("Musify.Domain.Entities.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Musify.Domain.Entities.UserHasTrack", b =>
|
||||
{
|
||||
b.HasOne("Musify.Domain.Entities.Track", "Track")
|
||||
.WithMany("UserTracks")
|
||||
.HasForeignKey("TrackId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Musify.Domain.Entities.User", "User")
|
||||
.WithMany("UserTracks")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Track");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Musify.Domain.Entities.Artist", b =>
|
||||
{
|
||||
b.Navigation("TrackArtists");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Musify.Domain.Entities.PlayList", b =>
|
||||
{
|
||||
b.Navigation("PlayListTracks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Musify.Domain.Entities.Track", b =>
|
||||
{
|
||||
b.Navigation("ListeningHistories");
|
||||
|
||||
b.Navigation("PlayListTracks");
|
||||
|
||||
b.Navigation("TrackArtists");
|
||||
|
||||
b.Navigation("UserTracks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Musify.Domain.Entities.User", b =>
|
||||
{
|
||||
b.Navigation("ListeningHistories");
|
||||
|
||||
b.Navigation("PlayLists");
|
||||
|
||||
b.Navigation("UserTracks");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Musify.Infrastructure.Persistence.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddArtistsAndTrackOwnership : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<long>(
|
||||
name: "OwnerUserId",
|
||||
table: "Tracks",
|
||||
type: "bigint",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Artists",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
Name = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
|
||||
NormalizedName = table.Column<string>(type: "character varying(200)", maxLength: 200, nullable: false),
|
||||
UserId = table.Column<long>(type: "bigint", nullable: true),
|
||||
ExternalId = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: true),
|
||||
CreatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "timestamp with time zone", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Artists", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_Artists_User_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "User",
|
||||
principalColumn: "Id");
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "TrackArtist",
|
||||
columns: table => new
|
||||
{
|
||||
TrackId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
ArtistId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
Position = table.Column<int>(type: "integer", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_TrackArtist", x => new { x.TrackId, x.ArtistId });
|
||||
table.ForeignKey(
|
||||
name: "FK_TrackArtist_Artists_ArtistId",
|
||||
column: x => x.ArtistId,
|
||||
principalTable: "Artists",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_TrackArtist_Tracks_TrackId",
|
||||
column: x => x.TrackId,
|
||||
principalTable: "Tracks",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Tracks_OwnerUserId",
|
||||
table: "Tracks",
|
||||
column: "OwnerUserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Artists_ExternalId",
|
||||
table: "Artists",
|
||||
column: "ExternalId",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Artists_NormalizedName",
|
||||
table: "Artists",
|
||||
column: "NormalizedName",
|
||||
unique: true,
|
||||
filter: "\"ExternalId\" IS NULL AND \"UserId\" IS NULL");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Artists_UserId",
|
||||
table: "Artists",
|
||||
column: "UserId",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_TrackArtist_ArtistId",
|
||||
table: "TrackArtist",
|
||||
column: "ArtistId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Tracks_User_OwnerUserId",
|
||||
table: "Tracks",
|
||||
column: "OwnerUserId",
|
||||
principalTable: "User",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
|
||||
migrationBuilder.Sql(
|
||||
"""
|
||||
UPDATE "Tracks" t SET "OwnerUserId" = ut."UserId"
|
||||
FROM (SELECT "TrackId", MIN("UserId") AS "UserId" FROM "UserHasTrack" GROUP BY "TrackId") ut
|
||||
WHERE t."Source" = 0 AND ut."TrackId" = t."Id";
|
||||
""");
|
||||
|
||||
migrationBuilder.Sql(
|
||||
"""
|
||||
INSERT INTO "Artists" ("Id", "Name", "NormalizedName", "UserId", "ExternalId", "CreatedAt", "UpdatedAt")
|
||||
SELECT gen_random_uuid(), u."Name", u."NormalizedName", u."Id", NULL, now(), now()
|
||||
FROM "User" u
|
||||
WHERE EXISTS (SELECT 1 FROM "Tracks" t WHERE t."OwnerUserId" = u."Id");
|
||||
""");
|
||||
|
||||
migrationBuilder.Sql(
|
||||
"""
|
||||
INSERT INTO "Artists" ("Id", "Name", "NormalizedName", "UserId", "ExternalId", "CreatedAt", "UpdatedAt")
|
||||
SELECT gen_random_uuid(), s."Artist", upper(s."Artist"), NULL, NULL, now(), now()
|
||||
FROM (SELECT DISTINCT "Artist" FROM "Tracks" WHERE "Source" = 1 AND "Artist" IS NOT NULL) s;
|
||||
""");
|
||||
|
||||
migrationBuilder.Sql(
|
||||
"""
|
||||
INSERT INTO "TrackArtist" ("TrackId", "ArtistId", "Position")
|
||||
SELECT t."Id", a."Id", 0 FROM "Tracks" t
|
||||
JOIN "Artists" a ON a."UserId" = t."OwnerUserId"
|
||||
WHERE t."Source" = 0;
|
||||
""");
|
||||
|
||||
migrationBuilder.Sql(
|
||||
"""
|
||||
INSERT INTO "TrackArtist" ("TrackId", "ArtistId", "Position")
|
||||
SELECT t."Id", a."Id", 0 FROM "Tracks" t
|
||||
JOIN "Artists" a ON a."UserId" IS NULL AND a."ExternalId" IS NULL AND a."Name" = t."Artist"
|
||||
WHERE t."Source" = 1 AND t."Artist" IS NOT NULL;
|
||||
""");
|
||||
|
||||
migrationBuilder.AddCheckConstraint(
|
||||
name: "CK_Tracks_OwnerUserId_Source",
|
||||
table: "Tracks",
|
||||
sql: "(\"OwnerUserId\" IS NOT NULL) = (\"Source\" = 0)");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Tracks_User_OwnerUserId",
|
||||
table: "Tracks");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "TrackArtist");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Artists");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Tracks_OwnerUserId",
|
||||
table: "Tracks");
|
||||
|
||||
migrationBuilder.DropCheckConstraint(
|
||||
name: "CK_Tracks_OwnerUserId_Source",
|
||||
table: "Tracks");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "OwnerUserId",
|
||||
table: "Tracks");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user