"""API Key database model for programmatic access.""" import uuid import secrets from datetime import datetime from sqlalchemy import Column, String, Boolean, ForeignKey, Text from sqlalchemy.sql import func from sqlalchemy.types import DateTime from app.db.base import Base def generate_api_key() -> str: """Generate a secure API key.""" return f"sk_{secrets.token_urlsafe(32)}" def generate_key_prefix(key: str) -> str: """Generate a display prefix for the key (first 8 chars after sk_).""" return key[:11] + "..." if len(key) > 11 else key class APIKey(Base): """API Key model for programmatic authentication.""" __tablename__ = "api_keys" id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4())) # Owner user_id = Column(String(36), ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True) # Key info name = Column(String(100), nullable=False) # User-friendly name key_hash = Column(String(255), nullable=False, unique=True) # Hashed key (for lookup) key_prefix = Column(String(20), nullable=False) # First chars for display (sk_xxx...) # Permissions & scope scopes = Column(Text, nullable=True) # JSON array of allowed scopes/permissions # Status is_active = Column(Boolean, default=True, nullable=False) # Usage tracking last_used_at = Column(DateTime, nullable=True) last_used_ip = Column(String(45), nullable=True) usage_count = Column(String(20), default="0") # String for SQLite compatibility # Expiration expires_at = Column(DateTime, nullable=True) # null = never expires # Timestamps created_at = Column(DateTime, server_default=func.now(), nullable=False) updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now(), nullable=False) def __repr__(self): return f"" @property def is_expired(self) -> bool: """Check if key is expired.""" if self.expires_at is None: return False return datetime.utcnow() > self.expires_at @property def is_valid(self) -> bool: """Check if key is valid (active and not expired).""" return self.is_active and not self.is_expired