"""Credential model - encrypted cookie/token storage.""" from datetime import datetime from typing import Optional from sqlalchemy import String, LargeBinary, UniqueConstraint from sqlalchemy.orm import Mapped, mapped_column from app.models.base import Base, TimestampMixin class CredentialType: """Credential type constants.""" COOKIES = "cookies" TOKEN = "token" class Credential(Base, TimestampMixin): """Encrypted credential storage for platform authentication.""" __tablename__ = "credentials" __table_args__ = (UniqueConstraint("platform", name="uq_credential_platform"),) id: Mapped[int] = mapped_column(primary_key=True) platform: Mapped[str] = mapped_column(String(50), nullable=False, index=True) credential_type: Mapped[str] = mapped_column(String(20), nullable=False) # Encrypted credential data data: Mapped[bytes] = mapped_column(LargeBinary, nullable=False) # Status tracking expires_at: Mapped[Optional[datetime]] = mapped_column(nullable=True) last_verified: Mapped[Optional[datetime]] = mapped_column(nullable=True) def __repr__(self) -> str: return f"" def to_dict(self, include_data: bool = False) -> dict: """Convert to dictionary for API responses. By default, does NOT include the encrypted data for security. """ result = { "id": self.id, "platform": self.platform, "credential_type": self.credential_type, "expires_at": self.expires_at.isoformat() if self.expires_at else None, "last_verified": self.last_verified.isoformat() if self.last_verified else None, "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_data: result["data"] = self.data return result