54 lines
1.9 KiB
Python
54 lines
1.9 KiB
Python
"""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"<Credential {self.platform} ({self.credential_type})>"
|
|
|
|
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
|