Add organization management and admin-specific APIs

- Introduced schemas for organizations, including creation, updates, and responses.
- Created models for `Organization` and `UserOrganization` with role-based access control and relationships.
- Implemented admin APIs for managing users, organizations, and bulk actions.
- Added advanced filtering, sorting, and pagination for user and organization queries.
- Updated `CRUD` logic to support organization-specific operations and member management.
- Enhanced database with necessary indexes and validation for improved performance and data integrity.
This commit is contained in:
Felipe Cardoso
2025-10-31 12:18:43 +01:00
parent e19026453f
commit 2d909774df
13 changed files with 1952 additions and 2 deletions

View File

@@ -9,8 +9,11 @@ from .base import TimestampMixin, UUIDMixin
# Import models
from .user import User
from .user_session import UserSession
from .organization import Organization
from .user_organization import UserOrganization, OrganizationRole
__all__ = [
'Base', 'TimestampMixin', 'UUIDMixin',
'User', 'UserSession',
'Organization', 'UserOrganization', 'OrganizationRole',
]

View File

@@ -0,0 +1,31 @@
# app/models/organization.py
from sqlalchemy import Column, String, Boolean, Text, Index
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import relationship
from .base import Base, TimestampMixin, UUIDMixin
class Organization(Base, UUIDMixin, TimestampMixin):
"""
Organization model for multi-tenant support.
Users can belong to multiple organizations with different roles.
"""
__tablename__ = 'organizations'
name = Column(String(255), nullable=False, index=True)
slug = Column(String(255), unique=True, nullable=False, index=True)
description = Column(Text, nullable=True)
is_active = Column(Boolean, default=True, nullable=False, index=True)
settings = Column(JSONB, default={})
# Relationships
user_organizations = relationship("UserOrganization", back_populates="organization", cascade="all, delete-orphan")
__table_args__ = (
Index('ix_organizations_name_active', 'name', 'is_active'),
Index('ix_organizations_slug_active', 'slug', 'is_active'),
)
def __repr__(self):
return f"<Organization {self.name} ({self.slug})>"

View File

@@ -1,5 +1,6 @@
from sqlalchemy import Column, String, Boolean, DateTime
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import relationship
from .base import Base, TimestampMixin, UUIDMixin
@@ -17,5 +18,8 @@ class User(Base, UUIDMixin, TimestampMixin):
preferences = Column(JSONB)
deleted_at = Column(DateTime(timezone=True), nullable=True, index=True)
# Relationships
user_organizations = relationship("UserOrganization", back_populates="user", cascade="all, delete-orphan")
def __repr__(self):
return f"<User {self.email}>"

View File

@@ -0,0 +1,50 @@
# app/models/user_organization.py
from enum import Enum as PyEnum
from sqlalchemy import Column, ForeignKey, Boolean, String, Index, Enum
from sqlalchemy.dialects.postgresql import UUID as PGUUID
from sqlalchemy.orm import relationship
from .base import Base, TimestampMixin
class OrganizationRole(str, PyEnum):
"""
Built-in organization roles.
These provide a baseline role system that can be optionally used.
Projects can extend this or implement their own permission system.
"""
OWNER = "owner" # Full control over organization
ADMIN = "admin" # Can manage users and settings
MEMBER = "member" # Regular member with standard access
GUEST = "guest" # Limited read-only access
class UserOrganization(Base, TimestampMixin):
"""
Junction table for many-to-many relationship between Users and Organizations.
Includes role information for flexible RBAC.
"""
__tablename__ = 'user_organizations'
user_id = Column(PGUUID(as_uuid=True), ForeignKey('users.id', ondelete='CASCADE'), primary_key=True)
organization_id = Column(PGUUID(as_uuid=True), ForeignKey('organizations.id', ondelete='CASCADE'), primary_key=True)
role = Column(Enum(OrganizationRole), default=OrganizationRole.MEMBER, nullable=False, index=True)
is_active = Column(Boolean, default=True, nullable=False, index=True)
# Optional: Custom permissions override for specific users
custom_permissions = Column(String(500), nullable=True) # JSON array of permission strings
# Relationships
user = relationship("User", back_populates="user_organizations")
organization = relationship("Organization", back_populates="user_organizations")
__table_args__ = (
Index('ix_user_org_user_active', 'user_id', 'is_active'),
Index('ix_user_org_org_active', 'organization_id', 'is_active'),
Index('ix_user_org_role', 'role'),
)
def __repr__(self):
return f"<UserOrganization user={self.user_id} org={self.organization_id} role={self.role}>"