import enum import uuid from datetime import datetime from sqlalchemy import ForeignKey, Index, Integer, String, Text, func from sqlalchemy.dialects.postgresql import ENUM from sqlalchemy.orm import Mapped, mapped_column, relationship from proxy_pool.db.base import Base, TimestampMixin, UUIDPrimaryKeyMixin class CreditTxType(str, enum.StrEnum): PURCHASE = "purchase" ACQUIRE = "acquire" REFUND = "refund" ADMIN_ADJUST = "admin_adjust" class User(UUIDPrimaryKeyMixin, TimestampMixin, Base): __tablename__ = "users" email: Mapped[str] = mapped_column(String(320), unique=True) display_name: Mapped[str | None] = mapped_column(String(128)) is_active: Mapped[bool] = mapped_column(default=True) api_keys: Mapped[list["ApiKey"]] = relationship(back_populates="user") credit_entries: Mapped[list["CreditLedger"]] = relationship(back_populates="user") class ApiKey(UUIDPrimaryKeyMixin, TimestampMixin, Base): __tablename__ = "api_keys" __table_args__ = ( Index("ix_api_keys_hash", "key_hash", unique=True), Index("ix_api_keys_prefix", "prefix"), ) user_id: Mapped[uuid.UUID] = mapped_column( ForeignKey("users.id", ondelete="CASCADE"), ) key_hash: Mapped[str] = mapped_column(String(128)) prefix: Mapped[str] = mapped_column(String(8)) label: Mapped[str | None] = mapped_column(String(128)) is_active: Mapped[bool] = mapped_column(default=True) last_used_at: Mapped[datetime | None] = mapped_column() expires_at: Mapped[datetime | None] = mapped_column() user: Mapped["User"] = relationship(back_populates="api_keys") class CreditLedger(UUIDPrimaryKeyMixin, TimestampMixin, Base): __tablename__ = "credit_ledger" __table_args__ = (Index("ix_ledger_user_created", "user_id", "created_at"),) user_id: Mapped[uuid.UUID] = mapped_column( ForeignKey("users.id", ondelete="CASCADE"), ) amount: Mapped[int] = mapped_column(Integer) tx_type: Mapped[CreditTxType] = mapped_column( ENUM(CreditTxType, name="credit_tx_type"), ) description: Mapped[str | None] = mapped_column(Text) reference_id: Mapped[uuid.UUID | None] = mapped_column() user: Mapped["User"] = relationship(back_populates="credit_entries") class ProxyLease(UUIDPrimaryKeyMixin, Base): __tablename__ = "proxy_leases" __table_args__ = ( Index("ix_leases_user", "user_id"), Index("ix_leases_proxy_active", "proxy_id", "is_released"), ) user_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("users.id")) proxy_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("proxies.id")) acquired_at: Mapped[datetime] = mapped_column(server_default=func.now()) expires_at: Mapped[datetime] = mapped_column() is_released: Mapped[bool] = mapped_column(default=False)