feat(creator): Refactor Creator flow, implement book creation pipeline & versioning, and setup Docker staging

- Relocate dashboard routing to /creator and editor workspace to /creator/edit/{BookId}
- Implement CreateBookCommand and handler with transactional default chapter seeding
- Implement PublishBookVersionCommand and GetCreatorDashboardDataQuery
- Build CreatorDashboard modal and UI components with customized dark input styles
- Add run-stage.sh script to automate staging environment setup, database migrations, and health checks
- Update developer workflow rules in GEMINI.md
This commit is contained in:
2026-06-14 10:58:37 +02:00
parent 978485e8ff
commit 8856fb1614
29 changed files with 4656 additions and 388 deletions
@@ -172,6 +172,103 @@ namespace NexusReader.Data.Migrations
b.ToTable("Authors");
});
modelBuilder.Entity("NexusReader.Domain.Entities.Book", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid?>("CurrentDraftRevisionId")
.HasColumnType("uuid");
b.Property<Guid?>("LivePublishedRevisionId")
.HasColumnType("uuid");
b.Property<string>("TenantId")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("character varying(128)");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("character varying(255)");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("CurrentDraftRevisionId");
b.HasIndex("LivePublishedRevisionId");
b.HasIndex("TenantId");
b.HasIndex("UserId");
b.ToTable("Books");
});
modelBuilder.Entity("NexusReader.Domain.Entities.BookRevision", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("BookId")
.HasColumnType("uuid");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<bool>("IsPublished")
.HasColumnType("boolean");
b.Property<DateTime?>("PublishedAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("VersionString")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("character varying(100)");
b.HasKey("Id");
b.HasIndex("BookId");
b.ToTable("BookRevisions");
});
modelBuilder.Entity("NexusReader.Domain.Entities.Chapter", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid>("BookRevisionId")
.HasColumnType("uuid");
b.Property<string>("MarkdownContent")
.IsRequired()
.HasColumnType("text");
b.Property<int>("SortOrder")
.HasColumnType("integer");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("character varying(255)");
b.HasKey("Id");
b.HasIndex("BookRevisionId");
b.ToTable("Chapters");
});
modelBuilder.Entity("NexusReader.Domain.Entities.Ebook", b =>
{
b.Property<Guid>("Id")
@@ -614,6 +711,53 @@ namespace NexusReader.Data.Migrations
.IsRequired();
});
modelBuilder.Entity("NexusReader.Domain.Entities.Book", b =>
{
b.HasOne("NexusReader.Domain.Entities.BookRevision", "CurrentDraftRevision")
.WithMany()
.HasForeignKey("CurrentDraftRevisionId")
.OnDelete(DeleteBehavior.Restrict);
b.HasOne("NexusReader.Domain.Entities.BookRevision", "LivePublishedRevision")
.WithMany()
.HasForeignKey("LivePublishedRevisionId")
.OnDelete(DeleteBehavior.Restrict);
b.HasOne("NexusReader.Domain.Entities.NexusUser", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("CurrentDraftRevision");
b.Navigation("LivePublishedRevision");
b.Navigation("User");
});
modelBuilder.Entity("NexusReader.Domain.Entities.BookRevision", b =>
{
b.HasOne("NexusReader.Domain.Entities.Book", "Book")
.WithMany("Revisions")
.HasForeignKey("BookId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Book");
});
modelBuilder.Entity("NexusReader.Domain.Entities.Chapter", b =>
{
b.HasOne("NexusReader.Domain.Entities.BookRevision", "BookRevision")
.WithMany("Chapters")
.HasForeignKey("BookRevisionId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("BookRevision");
});
modelBuilder.Entity("NexusReader.Domain.Entities.Ebook", b =>
{
b.HasOne("NexusReader.Domain.Entities.Author", "Author")
@@ -689,6 +833,16 @@ namespace NexusReader.Data.Migrations
b.Navigation("Ebooks");
});
modelBuilder.Entity("NexusReader.Domain.Entities.Book", b =>
{
b.Navigation("Revisions");
});
modelBuilder.Entity("NexusReader.Domain.Entities.BookRevision", b =>
{
b.Navigation("Chapters");
});
modelBuilder.Entity("NexusReader.Domain.Entities.KnowledgeUnit", b =>
{
b.Navigation("IncomingLinks");