"""User, Role, and UserRole models — supports multiple admin roles.""" from datetime import datetime from sqlalchemy import ( Boolean, Column, DateTime, ForeignKey, Integer, String, Table, Text, UniqueConstraint, ) from sqlalchemy.orm import DeclarativeBase, relationship from app.models import Base # ── Many-to-many: users ↔ roles ────────────────────────────────────────────── user_roles = Table( "user_roles", Base.metadata, Column("user_id", Integer, ForeignKey("users.id", ondelete="CASCADE"), primary_key=True), Column("role_id", Integer, ForeignKey("roles.id", ondelete="CASCADE"), primary_key=True), ) # ── Role ───────────────────────────────────────────────────────────────────── class Role(Base): __tablename__ = "roles" id = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(50), unique=True, nullable=False) description = Column(Text, nullable=True) users = relationship("User", secondary=user_roles, back_populates="roles") # ── User ───────────────────────────────────────────────────────────────────── class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True, autoincrement=True) email = Column(String(255), unique=True, nullable=False) display_name = Column(String(100), nullable=False) avatar_url = Column(String(500), nullable=True) auth_provider = Column(String(20), nullable=False, default="google") password_hash = Column(String(255), nullable=True) is_admin = Column(Boolean, default=False) created_at = Column(DateTime, nullable=False, default=datetime.utcnow) # Role-based admin (many-to-many) roles = relationship("Role", secondary=user_roles, back_populates="users") @property def is_admin_effective(self) -> bool: """True when is_admin=True (legacy) OR the user has an 'admin' role.""" if self.is_admin: return True return any(role.name == "admin" for role in self.roles)