Wire production domain, CORS for tenant subdomains, celery volume mounts, and nginx reverse proxy configs for apex, API, identity, auth, and wildcard tenants. Co-authored-by: Cursor <cursoragent@cursor.com>
39 lines
1.5 KiB
Python
39 lines
1.5 KiB
Python
import uuid
|
|
from datetime import datetime
|
|
|
|
from sqlalchemy import Boolean, DateTime, Enum as SAEnum, ForeignKey, Index, UniqueConstraint, func
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from app.core.database import Base
|
|
from app.models.types import GUID, MembershipRole
|
|
|
|
|
|
class TenantMembership(Base):
|
|
"""عضویت کاربر در یک tenant — tenant_id فقط UUID مرجع Core است (بدون FK بین DB)."""
|
|
|
|
__tablename__ = "tenant_memberships"
|
|
|
|
id: Mapped[uuid.UUID] = mapped_column(GUID(), primary_key=True, default=uuid.uuid4)
|
|
tenant_id: Mapped[uuid.UUID] = mapped_column(GUID(), nullable=False)
|
|
user_id: Mapped[uuid.UUID] = mapped_column(
|
|
GUID(), ForeignKey("user_profiles.id", ondelete="CASCADE"), nullable=False
|
|
)
|
|
role: Mapped[MembershipRole] = mapped_column(
|
|
SAEnum(
|
|
MembershipRole,
|
|
name="membership_role",
|
|
values_callable=lambda enum_cls: [item.value for item in enum_cls],
|
|
),
|
|
default=MembershipRole.TENANT_MEMBER,
|
|
)
|
|
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
|
|
joined_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), server_default=func.now()
|
|
)
|
|
|
|
__table_args__ = (
|
|
UniqueConstraint("tenant_id", "user_id", name="uq_tenant_membership"),
|
|
Index("ix_tenant_memberships_tenant_id", "tenant_id"),
|
|
Index("ix_tenant_memberships_user_id", "user_id"),
|
|
)
|