- Add User model with email unique constraint and relationships - Add ApiKey model with encrypted key storage and user relationship - Add UsageStats model with unique constraint (api_key_id, date, model) - Add ApiToken model with token_hash indexing - Configure all cascade delete relationships - Add 49 comprehensive tests with 95% coverage Models: - User: id, email, password_hash, created_at, updated_at, is_active - ApiKey: id, user_id, name, key_encrypted, is_active, created_at, last_used_at - UsageStats: id, api_key_id, date, model, requests_count, tokens_input, tokens_output, cost - ApiToken: id, user_id, token_hash, name, created_at, last_used_at, is_active Tests: 49 passed, coverage 95%
40 lines
1.4 KiB
Python
40 lines
1.4 KiB
Python
"""ApiKey model for OpenRouter API Key Monitor.
|
|
|
|
T08: ApiKey SQLAlchemy model
|
|
"""
|
|
from datetime import datetime
|
|
from sqlalchemy import Column, Integer, String, DateTime, Boolean, ForeignKey
|
|
from sqlalchemy.orm import relationship
|
|
|
|
from openrouter_monitor.database import Base
|
|
|
|
|
|
class ApiKey(Base):
|
|
"""API Key model for storing encrypted OpenRouter API keys.
|
|
|
|
Attributes:
|
|
id: Primary key
|
|
user_id: Foreign key to users table
|
|
name: Human-readable name for the key
|
|
key_encrypted: AES-256 encrypted API key
|
|
is_active: Whether the key is active
|
|
created_at: Timestamp when key was created
|
|
last_used_at: Timestamp when key was last used
|
|
user: Relationship to user
|
|
usage_stats: Relationship to usage statistics
|
|
"""
|
|
|
|
__tablename__ = "api_keys"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
name = Column(String(100), nullable=False)
|
|
key_encrypted = Column(String, nullable=False)
|
|
is_active = Column(Boolean, default=True, index=True)
|
|
created_at = Column(DateTime, default=datetime.utcnow)
|
|
last_used_at = Column(DateTime, nullable=True)
|
|
|
|
# Relationships
|
|
user = relationship("User", back_populates="api_keys", lazy="selectin")
|
|
usage_stats = relationship("UsageStats", back_populates="api_key", cascade="all, delete-orphan", lazy="selectin")
|