geo/backend/app/models/user.py

78 lines
3.4 KiB
Python

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(timezone=True), nullable=True)
createdAt: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
updatedAt: Mapped[datetime] = mapped_column(DateTime(timezone=True), 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(timezone=True), 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