"""User-related models""" from datetime import datetime from typing import Optional, List, TYPE_CHECKING from sqlalchemy import String, Integer, Boolean, Text, DateTime, ForeignKey from sqlalchemy.orm import Mapped, mapped_column, relationship from luxx.core.database import Base # Avoid circular import at runtime if TYPE_CHECKING: from luxx.models.chat import Conversation def local_now(): return datetime.now() class User(Base): """User model""" __tablename__ = "users" id: Mapped[int] = mapped_column(Integer, primary_key=True) username: Mapped[str] = mapped_column(String(50), unique=True, nullable=False) email: Mapped[Optional[str]] = mapped_column(String(120), unique=True, nullable=True) password_hash: Mapped[Optional[str]] = mapped_column(String(255), nullable=True) role: Mapped[str] = mapped_column(String(20), default="user") permission_level: Mapped[int] = mapped_column(Integer, default=1) workspace_path: Mapped[Optional[str]] = mapped_column(String(500), nullable=True) is_active: Mapped[bool] = mapped_column(Boolean, default=True) created_at: Mapped[datetime] = mapped_column(DateTime, default=local_now) # Relationships conversations: Mapped[List["Conversation"]] = relationship( "Conversation", back_populates="user", cascade="all, delete-orphan" ) llm_providers: Mapped[List["LLMProvider"]] = relationship("LLMProvider", back_populates="user") projects: Mapped[List["Project"]] = relationship("Project", back_populates="user") def to_dict(self): return { "id": self.id, "username": self.username, "email": self.email, "role": self.role, "permission_level": self.permission_level, "workspace_path": self.workspace_path, "is_active": self.is_active, "created_at": self.created_at.isoformat() if self.created_at else None } class LLMProvider(Base): """LLM Provider configuration model""" __tablename__ = "llm_providers" id: Mapped[int] = mapped_column(Integer, primary_key=True) user_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), nullable=False) name: Mapped[str] = mapped_column(String(100), nullable=False) provider_type: Mapped[str] = mapped_column(String(50), nullable=False, default="openai") base_url: Mapped[str] = mapped_column(String(500), nullable=False) api_key: Mapped[str] = mapped_column(String(500), nullable=False) default_model: Mapped[str] = mapped_column(String(100), nullable=False, default="gpt-4") max_tokens: Mapped[int] = mapped_column(Integer, default=8192) is_default: Mapped[bool] = mapped_column(Boolean, default=False) enabled: Mapped[bool] = mapped_column(Boolean, default=True) created_at: Mapped[datetime] = mapped_column(DateTime, default=local_now) updated_at: Mapped[datetime] = mapped_column(DateTime, default=local_now, onupdate=local_now) # Relationships user: Mapped["User"] = relationship("User", back_populates="llm_providers") def to_dict(self, include_key: bool = False): result = { "id": self.id, "user_id": self.user_id, "name": self.name, "provider_type": self.provider_type, "base_url": self.base_url, "default_model": self.default_model, "max_tokens": self.max_tokens, "is_default": self.is_default, "enabled": self.enabled, "created_at": self.created_at.isoformat() if self.created_at else None, "updated_at": self.updated_at.isoformat() if self.updated_at else None } if include_key: result["api_key"] = self.api_key return result class Project(Base): """Project model""" __tablename__ = "projects" id: Mapped[str] = mapped_column(String(64), primary_key=True) user_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id"), nullable=False) name: Mapped[str] = mapped_column(String(255), nullable=False) description: Mapped[Optional[str]] = mapped_column(Text, nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime, default=local_now) updated_at: Mapped[datetime] = mapped_column(DateTime, default=local_now, onupdate=local_now) # Relationships user: Mapped["User"] = relationship("User", back_populates="projects")