import uuid from datetime import datetime, timezone from sqlalchemy import String, Boolean, Integer, DateTime, Text, func, ForeignKey, Uuid from sqlalchemy.orm import Mapped, mapped_column, relationship from app.database import Base class User(Base): __tablename__ = "users" __table_args__ = {"extend_existing": True} id: Mapped[str] = mapped_column(Text, primary_key=True) email: Mapped[str] = mapped_column(Text, unique=True, nullable=False) phone: Mapped[str | None] = mapped_column(Text, nullable=True) username: Mapped[str | None] = mapped_column(Text, nullable=True) password: Mapped[str] = mapped_column(Text, nullable=False) firstName: Mapped[str | None] = mapped_column(Text, nullable=True) lastName: Mapped[str | None] = mapped_column(Text, nullable=True) avatar: Mapped[str | None] = mapped_column(Text, nullable=True) isActive: Mapped[bool] = mapped_column(Boolean, default=True) emailVerified: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) phoneVerified: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) lastLoginAt: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) createdAt: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), nullable=False) updatedAt: Mapped[datetime] = mapped_column(DateTime, default=func.now(), onupdate=func.now(), nullable=False) mfaSecret: Mapped[str | None] = mapped_column(Text, nullable=True) mfaEnabled: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) loginAttempts: Mapped[int] = mapped_column(Integer, default=0, nullable=False) lockedUntil: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) organization_id: Mapped[uuid.UUID | None] = mapped_column(Uuid(as_uuid=True), ForeignKey("organizations.id", ondelete="SET NULL"), nullable=True) role: Mapped[str] = mapped_column(String(20), server_default="owner", nullable=False) plan: Mapped[str] = mapped_column(String(20), server_default="free", nullable=False) max_queries: Mapped[int] = mapped_column(Integer, server_default="5", nullable=False) queries: Mapped[list["Query"]] = relationship( "Query", back_populates="user", viewonly=True, primaryjoin="User.id == foreign(Query.user_id)", ) subscriptions: Mapped[list["Subscription"]] = relationship( "Subscription", back_populates="user", viewonly=True, primaryjoin="User.id == foreign(Subscription.user_id)", ) organization: Mapped["Organization | None"] = relationship( "Organization", secondary="org_members", viewonly=True, uselist=False, ) org_memberships: Mapped[list["OrgMember"]] = relationship( "OrgMember", back_populates="user", viewonly=True, primaryjoin="User.id == foreign(OrgMember.user_id)", ) @property def name(self) -> str | None: if self.firstName and self.lastName: return f"{self.firstName} {self.lastName}" return self.username or self.firstName @property def is_admin(self) -> bool: return False @property def is_active(self) -> bool: return self.isActive @property def email_verified(self) -> bool: return self.emailVerified @property def avatar_url(self) -> str | None: return self.avatar @property def password_hash(self) -> str: return self.password