feat: add multi-platform CMS architecture (Phase 1)
Implement the foundation for multi-platform support allowing independent business offerings (OMS, Loyalty, etc.) with their own CMS pages. Database Models: - Add Platform model for business offerings (domain, branding, config) - Add VendorPlatform junction table for many-to-many relationship - Update SubscriptionTier with platform_id and CMS limits - Update ContentPage with platform_id, is_platform_page for three-tier hierarchy - Add CMS feature codes (cms_basic, cms_custom_pages, cms_templates, etc.) Three-Tier Content Resolution: 1. Vendor override (platform_id + vendor_id + slug) 2. Vendor default (platform_id + vendor_id=NULL + is_platform_page=False) 3. Platform marketing pages (is_platform_page=True) New Components: - PlatformContextMiddleware for detecting platform from domain/path - ContentPageService updated with full three-tier resolution - Platform folder structure (app/platforms/oms/, app/platforms/loyalty/) - Alembic migration with backfill for existing data Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -84,12 +84,26 @@ class SubscriptionTier(Base, TimestampMixin):
|
||||
Database-driven tier definitions with Stripe integration.
|
||||
|
||||
Replaces the hardcoded TIER_LIMITS dict for dynamic tier management.
|
||||
|
||||
Can be:
|
||||
- Global tier (platform_id=NULL): Available to all platforms
|
||||
- Platform-specific tier (platform_id set): Only for that platform
|
||||
"""
|
||||
|
||||
__tablename__ = "subscription_tiers"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
code = Column(String(30), unique=True, nullable=False, index=True)
|
||||
|
||||
# Platform association (NULL = global tier available to all platforms)
|
||||
platform_id = Column(
|
||||
Integer,
|
||||
ForeignKey("platforms.id", ondelete="CASCADE"),
|
||||
nullable=True,
|
||||
index=True,
|
||||
comment="Platform this tier belongs to (NULL = global tier)",
|
||||
)
|
||||
|
||||
code = Column(String(30), nullable=False, index=True)
|
||||
name = Column(String(100), nullable=False)
|
||||
description = Column(Text, nullable=True)
|
||||
|
||||
@@ -103,6 +117,18 @@ class SubscriptionTier(Base, TimestampMixin):
|
||||
team_members = Column(Integer, nullable=True)
|
||||
order_history_months = Column(Integer, nullable=True)
|
||||
|
||||
# CMS Limits (null = unlimited)
|
||||
cms_pages_limit = Column(
|
||||
Integer,
|
||||
nullable=True,
|
||||
comment="Total CMS pages limit (NULL = unlimited)",
|
||||
)
|
||||
cms_custom_pages_limit = Column(
|
||||
Integer,
|
||||
nullable=True,
|
||||
comment="Custom pages limit, excluding overrides (NULL = unlimited)",
|
||||
)
|
||||
|
||||
# Features (JSON array of feature codes)
|
||||
features = Column(JSON, default=list)
|
||||
|
||||
@@ -116,8 +142,21 @@ class SubscriptionTier(Base, TimestampMixin):
|
||||
is_active = Column(Boolean, default=True, nullable=False)
|
||||
is_public = Column(Boolean, default=True, nullable=False) # False for enterprise
|
||||
|
||||
# Relationship to Platform
|
||||
platform = relationship(
|
||||
"Platform",
|
||||
back_populates="subscription_tiers",
|
||||
foreign_keys=[platform_id],
|
||||
)
|
||||
|
||||
# Unique constraint: tier code must be unique per platform (or globally if NULL)
|
||||
__table_args__ = (
|
||||
Index("idx_tier_platform_active", "platform_id", "is_active"),
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<SubscriptionTier(code='{self.code}', name='{self.name}')>"
|
||||
platform_info = f", platform_id={self.platform_id}" if self.platform_id else ""
|
||||
return f"<SubscriptionTier(code='{self.code}', name='{self.name}'{platform_info})>"
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""Convert tier to dictionary (compatible with TIER_LIMITS format)."""
|
||||
@@ -129,6 +168,8 @@ class SubscriptionTier(Base, TimestampMixin):
|
||||
"products_limit": self.products_limit,
|
||||
"team_members": self.team_members,
|
||||
"order_history_months": self.order_history_months,
|
||||
"cms_pages_limit": self.cms_pages_limit,
|
||||
"cms_custom_pages_limit": self.cms_custom_pages_limit,
|
||||
"features": self.features or [],
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user