feat: add database backed membership tiers
All checks were successful
deploy-socialize / image (push) Successful in 1m9s
deploy-socialize / deploy (push) Successful in 19s

This commit is contained in:
2026-05-07 20:29:53 -04:00
parent db16e79d9f
commit 6d92119c9c
23 changed files with 3512 additions and 30 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,118 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional
namespace Socialize.Api.Migrations
{
/// <inheritdoc />
internal partial class AddOrganizationMembershipTiers : Migration
{
private static readonly string[] MembershipTierSeedColumns =
[
"Id",
"ActiveContentLimit",
"Description",
"ExternalReviewerLimit",
"IsCustom",
"Key",
"MemberLimit",
"MonthlyPriceCents",
"Name",
"SortOrder",
"WorkspaceLimit"
];
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
ArgumentNullException.ThrowIfNull(migrationBuilder);
migrationBuilder.AddColumn<Guid>(
name: "MembershipTierId",
table: "Organizations",
type: "uuid",
nullable: false,
defaultValue: new Guid("20000000-0000-0000-0000-000000000001"));
migrationBuilder.CreateTable(
name: "OrganizationMembershipTiers",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
Key = table.Column<string>(type: "character varying(64)", maxLength: 64, nullable: false),
Name = table.Column<string>(type: "character varying(128)", maxLength: 128, nullable: false),
Description = table.Column<string>(type: "character varying(512)", maxLength: 512, nullable: false),
MonthlyPriceCents = table.Column<int>(type: "integer", nullable: true),
WorkspaceLimit = table.Column<int>(type: "integer", nullable: true),
ActiveContentLimit = table.Column<int>(type: "integer", nullable: true),
MemberLimit = table.Column<int>(type: "integer", nullable: true),
ExternalReviewerLimit = table.Column<int>(type: "integer", nullable: true),
IsCustom = table.Column<bool>(type: "boolean", nullable: false),
SortOrder = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_OrganizationMembershipTiers", x => x.Id);
});
migrationBuilder.InsertData(
table: "OrganizationMembershipTiers",
columns: MembershipTierSeedColumns,
values: new object[,]
{
{ new Guid("20000000-0000-0000-0000-000000000001"), 3, "For trying Socialize on one real approval workflow.", 1, false, "free", 2, 0, "Free", 10, 1 },
{ new Guid("20000000-0000-0000-0000-000000000002"), 25, "For solo operators managing recurring client reviews.", 10, false, "freelance", 5, 1900, "Freelance", 20, 3 },
{ new Guid("20000000-0000-0000-0000-000000000003"), 250, "For agencies that need repeatable client approval operations.", null, false, "agency", 25, 7900, "Agency", 30, 15 },
{ new Guid("20000000-0000-0000-0000-000000000004"), null, "For larger organizations with governance and access needs.", null, true, "enterprise", null, null, "Enterprise", 40, null }
});
migrationBuilder.CreateIndex(
name: "IX_Organizations_MembershipTierId",
table: "Organizations",
column: "MembershipTierId");
migrationBuilder.CreateIndex(
name: "IX_OrganizationMembershipTiers_Key",
table: "OrganizationMembershipTiers",
column: "Key",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_OrganizationMembershipTiers_SortOrder",
table: "OrganizationMembershipTiers",
column: "SortOrder");
migrationBuilder.AddForeignKey(
name: "FK_Organizations_OrganizationMembershipTiers_MembershipTierId",
table: "Organizations",
column: "MembershipTierId",
principalTable: "OrganizationMembershipTiers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
ArgumentNullException.ThrowIfNull(migrationBuilder);
migrationBuilder.DropForeignKey(
name: "FK_Organizations_OrganizationMembershipTiers_MembershipTierId",
table: "Organizations");
migrationBuilder.DropTable(
name: "OrganizationMembershipTiers");
migrationBuilder.DropIndex(
name: "IX_Organizations_MembershipTierId",
table: "Organizations");
migrationBuilder.DropColumn(
name: "MembershipTierId",
table: "Organizations");
}
}
}

View File

@@ -1659,6 +1659,11 @@ namespace Socialize.Api.Migrations
.HasMaxLength(2048)
.HasColumnType("character varying(2048)");
b.Property<Guid>("MembershipTierId")
.ValueGeneratedOnAdd()
.HasColumnType("uuid")
.HasDefaultValue(new Guid("20000000-0000-0000-0000-000000000001"));
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(256)
@@ -1669,6 +1674,8 @@ namespace Socialize.Api.Migrations
b.HasKey("Id");
b.HasIndex("MembershipTierId");
b.HasIndex("OwnerUserId");
b.ToTable("Organizations", (string)null);
@@ -1708,6 +1715,110 @@ namespace Socialize.Api.Migrations
b.ToTable("OrganizationMemberships", (string)null);
});
modelBuilder.Entity("Socialize.Api.Modules.Organizations.Data.OrganizationMembershipTier", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<int?>("ActiveContentLimit")
.HasColumnType("integer");
b.Property<string>("Description")
.IsRequired()
.HasMaxLength(512)
.HasColumnType("character varying(512)");
b.Property<int?>("ExternalReviewerLimit")
.HasColumnType("integer");
b.Property<bool>("IsCustom")
.HasColumnType("boolean");
b.Property<string>("Key")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("character varying(64)");
b.Property<int?>("MemberLimit")
.HasColumnType("integer");
b.Property<int?>("MonthlyPriceCents")
.HasColumnType("integer");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("character varying(128)");
b.Property<int>("SortOrder")
.HasColumnType("integer");
b.Property<int?>("WorkspaceLimit")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("Key")
.IsUnique();
b.HasIndex("SortOrder");
b.ToTable("OrganizationMembershipTiers", (string)null);
b.HasData(
new
{
Id = new Guid("20000000-0000-0000-0000-000000000001"),
ActiveContentLimit = 3,
Description = "For trying Socialize on one real approval workflow.",
ExternalReviewerLimit = 1,
IsCustom = false,
Key = "free",
MemberLimit = 2,
MonthlyPriceCents = 0,
Name = "Free",
SortOrder = 10,
WorkspaceLimit = 1
},
new
{
Id = new Guid("20000000-0000-0000-0000-000000000002"),
ActiveContentLimit = 25,
Description = "For solo operators managing recurring client reviews.",
ExternalReviewerLimit = 10,
IsCustom = false,
Key = "freelance",
MemberLimit = 5,
MonthlyPriceCents = 1900,
Name = "Freelance",
SortOrder = 20,
WorkspaceLimit = 3
},
new
{
Id = new Guid("20000000-0000-0000-0000-000000000003"),
ActiveContentLimit = 250,
Description = "For agencies that need repeatable client approval operations.",
IsCustom = false,
Key = "agency",
MemberLimit = 25,
MonthlyPriceCents = 7900,
Name = "Agency",
SortOrder = 30,
WorkspaceLimit = 15
},
new
{
Id = new Guid("20000000-0000-0000-0000-000000000004"),
Description = "For larger organizations with governance and access needs.",
IsCustom = true,
Key = "enterprise",
Name = "Enterprise",
SortOrder = 40
});
});
modelBuilder.Entity("Socialize.Api.Modules.Workspaces.Data.Workspace", b =>
{
b.Property<Guid>("Id")
@@ -2127,6 +2238,15 @@ namespace Socialize.Api.Migrations
.IsRequired();
});
modelBuilder.Entity("Socialize.Api.Modules.Organizations.Data.Organization", b =>
{
b.HasOne("Socialize.Api.Modules.Organizations.Data.OrganizationMembershipTier", null)
.WithMany()
.HasForeignKey("MembershipTierId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
});
modelBuilder.Entity("Socialize.Api.Modules.Organizations.Data.OrganizationMembership", b =>
{
b.HasOne("Socialize.Api.Modules.Organizations.Data.Organization", null)