from datetime import datetime from sqlalchemy import DateTime, ForeignKey, Index, Integer, Text from sqlalchemy.orm import Mapped, mapped_column from fabledassistant.models import Base from fabledassistant.models.base import CreatedAtMixin class ApiKey(Base, CreatedAtMixin): __tablename__ = "api_keys" id: Mapped[int] = mapped_column(primary_key=True) user_id: Mapped[int] = mapped_column( Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False ) name: Mapped[str] = mapped_column(Text, nullable=False) key_hash: Mapped[str] = mapped_column(Text, nullable=False, unique=True) key_prefix: Mapped[str] = mapped_column(Text, nullable=False) scope: Mapped[str] = mapped_column(Text, nullable=False) # "read" or "write" last_used_at: Mapped[datetime | None] = mapped_column( DateTime(timezone=True), nullable=True ) revoked_at: Mapped[datetime | None] = mapped_column( DateTime(timezone=True), nullable=True ) __table_args__ = ( Index("ix_api_keys_user_id", "user_id"), Index("ix_api_keys_key_hash", "key_hash"), ) def to_dict(self) -> dict: return { "id": self.id, "name": self.name, "key_prefix": self.key_prefix, "scope": self.scope, "last_used_at": self.last_used_at.isoformat() if self.last_used_at else None, "created_at": self.created_at.isoformat(), "revoked_at": self.revoked_at.isoformat() if self.revoked_at else None, }