Versioned PRODUCT.md library + marketplace — backend (Slice 4)

Mirrors the agent-profile stack for products: ProductProfile entity (org-scoped,
versioned by org+key+version; null org = free builtin), a PRODUCT.md parser + writer,
and endpoints — upload, list, marketplace, get, publish/unpublish, fork, install, and
apply-to-product (sets Product.Identity to the profile's PRODUCT.md). Reuses the shared
ProfileOrigin/Status/Visibility enums; product profiles are gated owner-level
(CreateProductsAndTeams). Adds the product_profiles table.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-15 20:40:57 +03:30
parent ad330641c3
commit bcdbc7e941
12 changed files with 1208 additions and 0 deletions
@@ -250,6 +250,79 @@ namespace TeamUp.Modules.OrgBoard.Persistence.Migrations
b.ToTable("products", "orgboard");
});
modelBuilder.Entity("TeamUp.Modules.OrgBoard.Domain.ProductProfile", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<Guid?>("AuthoredByMemberId")
.HasColumnType("uuid");
b.Property<string>("Body")
.IsRequired()
.HasColumnType("text");
b.Property<string>("ContentHash")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("character varying(64)");
b.Property<DateTimeOffset>("CreatedAtUtc")
.HasColumnType("timestamp with time zone");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("character varying(200)");
b.Property<Guid?>("OrganizationId")
.HasColumnType("uuid");
b.Property<string>("Origin")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<string>("ProfileKey")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("character varying(128)");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<string>("Summary")
.HasMaxLength(1000)
.HasColumnType("character varying(1000)");
b.Property<DateTimeOffset>("UpdatedAtUtc")
.HasColumnType("timestamp with time zone");
b.Property<string>("Version")
.IsRequired()
.HasMaxLength(32)
.HasColumnType("character varying(32)");
b.Property<string>("Visibility")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.HasKey("Id");
b.HasIndex("OrganizationId");
b.HasIndex("OrganizationId", "ProfileKey", "Version")
.IsUnique();
NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("OrganizationId", "ProfileKey", "Version"), false);
b.ToTable("product_profiles", "orgboard");
});
modelBuilder.Entity("TeamUp.Modules.OrgBoard.Domain.Seat", b =>
{
b.Property<Guid>("Id")