"""Fractafrag — SQLAlchemy ORM Models.""" import uuid from datetime import datetime from sqlalchemy import ( Column, String, Text, Boolean, Integer, Float, SmallInteger, ForeignKey, DateTime, UniqueConstraint, Index, CheckConstraint, ) from sqlalchemy.dialects.postgresql import UUID, JSONB, ARRAY from pgvector.sqlalchemy import Vector from sqlalchemy.orm import relationship from app.database import Base class User(Base): __tablename__ = "users" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) username = Column(String, unique=True, nullable=False, index=True) email = Column(String, unique=True, nullable=False, index=True) password_hash = Column(String, nullable=False) role = Column(String, nullable=False, default="user") trust_tier = Column(String, nullable=False, default="standard") stripe_customer_id = Column(String, nullable=True) subscription_tier = Column(String, default="free") ai_credits_remaining = Column(Integer, default=0) taste_vector = Column(Vector(512), nullable=True) # Creator economy stubs is_verified_creator = Column(Boolean, default=False) verified_creator_at = Column(DateTime(timezone=True), nullable=True) stripe_connect_account_id = Column(String, nullable=True) # Timestamps created_at = Column(DateTime(timezone=True), default=datetime.utcnow) last_active_at = Column(DateTime(timezone=True), nullable=True) # Relationships shaders = relationship("Shader", back_populates="author") votes = relationship("Vote", back_populates="user") api_keys = relationship("ApiKey", back_populates="user") class Shader(Base): __tablename__ = "shaders" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) author_id = Column(UUID(as_uuid=True), ForeignKey("users.id", ondelete="SET NULL"), nullable=True) title = Column(String, nullable=False) description = Column(Text, nullable=True) glsl_code = Column(Text, nullable=False) is_public = Column(Boolean, default=True) is_ai_generated = Column(Boolean, default=False) ai_provider = Column(String, nullable=True) thumbnail_url = Column(String, nullable=True) preview_url = Column(String, nullable=True) render_status = Column(String, default="pending") style_vector = Column(Vector(512), nullable=True) style_metadata = Column(JSONB, nullable=True) tags = Column(ARRAY(String), default=list) shader_type = Column(String, default="2d") forked_from = Column(UUID(as_uuid=True), ForeignKey("shaders.id", ondelete="SET NULL"), nullable=True) view_count = Column(Integer, default=0) score = Column(Float, default=0.0) # Creator economy stubs access_tier = Column(String, default="open") source_unlock_price_cents = Column(Integer, nullable=True) commercial_license_price_cents = Column(Integer, nullable=True) verified_creator_shader = Column(Boolean, default=False) # Timestamps created_at = Column(DateTime(timezone=True), default=datetime.utcnow) updated_at = Column(DateTime(timezone=True), default=datetime.utcnow, onupdate=datetime.utcnow) # Relationships author = relationship("User", back_populates="shaders") votes = relationship("Vote", back_populates="shader") class Vote(Base): __tablename__ = "votes" __table_args__ = (UniqueConstraint("user_id", "shader_id"),) id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) user_id = Column(UUID(as_uuid=True), ForeignKey("users.id", ondelete="CASCADE"), nullable=False) shader_id = Column(UUID(as_uuid=True), ForeignKey("shaders.id", ondelete="CASCADE"), nullable=False) value = Column(SmallInteger, nullable=False) created_at = Column(DateTime(timezone=True), default=datetime.utcnow) user = relationship("User", back_populates="votes") shader = relationship("Shader", back_populates="votes") class EngagementEvent(Base): __tablename__ = "engagement_events" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) user_id = Column(UUID(as_uuid=True), ForeignKey("users.id", ondelete="SET NULL"), nullable=True) session_id = Column(String, nullable=True) shader_id = Column(UUID(as_uuid=True), ForeignKey("shaders.id", ondelete="CASCADE"), nullable=False) event_type = Column(String, nullable=False) dwell_secs = Column(Float, nullable=True) metadata = Column(JSONB, nullable=True) created_at = Column(DateTime(timezone=True), default=datetime.utcnow) class Desire(Base): __tablename__ = "desires" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) author_id = Column(UUID(as_uuid=True), ForeignKey("users.id", ondelete="SET NULL"), nullable=True) prompt_text = Column(Text, nullable=False) prompt_embedding = Column(Vector(512), nullable=True) style_hints = Column(JSONB, nullable=True) tip_amount_cents = Column(Integer, default=0) status = Column(String, default="open") heat_score = Column(Float, default=1.0) fulfilled_by_shader = Column(UUID(as_uuid=True), ForeignKey("shaders.id", ondelete="SET NULL"), nullable=True) fulfilled_at = Column(DateTime(timezone=True), nullable=True) expires_at = Column(DateTime(timezone=True), nullable=True) created_at = Column(DateTime(timezone=True), default=datetime.utcnow) class DesireCluster(Base): __tablename__ = "desire_clusters" cluster_id = Column(UUID(as_uuid=True), primary_key=True) desire_id = Column(UUID(as_uuid=True), ForeignKey("desires.id", ondelete="CASCADE"), primary_key=True) similarity = Column(Float) class BountyTip(Base): __tablename__ = "bounty_tips" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) desire_id = Column(UUID(as_uuid=True), ForeignKey("desires.id", ondelete="CASCADE"), nullable=False) tipper_id = Column(UUID(as_uuid=True), ForeignKey("users.id", ondelete="SET NULL"), nullable=True) amount_cents = Column(Integer, nullable=False) stripe_payment_intent_id = Column(String, nullable=True) status = Column(String, default="held") created_at = Column(DateTime(timezone=True), default=datetime.utcnow) class CreatorPayout(Base): __tablename__ = "creator_payouts" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) creator_id = Column(UUID(as_uuid=True), ForeignKey("users.id", ondelete="SET NULL"), nullable=True) desire_id = Column(UUID(as_uuid=True), ForeignKey("desires.id", ondelete="SET NULL"), nullable=True) gross_amount_cents = Column(Integer) platform_fee_cents = Column(Integer) net_amount_cents = Column(Integer) stripe_transfer_id = Column(String, nullable=True) status = Column(String, default="pending") created_at = Column(DateTime(timezone=True), default=datetime.utcnow) class ApiKey(Base): __tablename__ = "api_keys" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) user_id = Column(UUID(as_uuid=True), ForeignKey("users.id", ondelete="CASCADE"), nullable=False) key_hash = Column(String, unique=True, nullable=False) key_prefix = Column(String, nullable=False) name = Column(String, nullable=True) trust_tier = Column(String, default="probation") submissions_approved = Column(Integer, default=0) rate_limit_per_hour = Column(Integer, default=10) last_used_at = Column(DateTime(timezone=True), nullable=True) created_at = Column(DateTime(timezone=True), default=datetime.utcnow) revoked_at = Column(DateTime(timezone=True), nullable=True) user = relationship("User", back_populates="api_keys") class GenerationLog(Base): __tablename__ = "generation_log" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) user_id = Column(UUID(as_uuid=True), ForeignKey("users.id", ondelete="SET NULL"), nullable=True) shader_id = Column(UUID(as_uuid=True), ForeignKey("shaders.id", ondelete="SET NULL"), nullable=True) provider = Column(String, nullable=False) prompt_text = Column(Text, nullable=True) tokens_used = Column(Integer, nullable=True) cost_cents = Column(Integer, nullable=True) success = Column(Boolean, nullable=True) created_at = Column(DateTime(timezone=True), default=datetime.utcnow) class Comment(Base): __tablename__ = "comments" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) shader_id = Column(UUID(as_uuid=True), ForeignKey("shaders.id", ondelete="CASCADE"), nullable=False) author_id = Column(UUID(as_uuid=True), ForeignKey("users.id", ondelete="SET NULL"), nullable=True) body = Column(Text, nullable=False) parent_id = Column(UUID(as_uuid=True), ForeignKey("comments.id", ondelete="CASCADE"), nullable=True) created_at = Column(DateTime(timezone=True), default=datetime.utcnow) # Creator economy stubs (dormant) class SourceUnlock(Base): __tablename__ = "source_unlocks" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) shader_id = Column(UUID(as_uuid=True), ForeignKey("shaders.id", ondelete="CASCADE"), nullable=False) buyer_id = Column(UUID(as_uuid=True), ForeignKey("users.id", ondelete="SET NULL"), nullable=True) license_type = Column(String, nullable=False) amount_cents = Column(Integer, nullable=False) platform_fee_cents = Column(Integer, nullable=False) stripe_payment_intent_id = Column(String, nullable=True) created_at = Column(DateTime(timezone=True), default=datetime.utcnow) class CreatorEngagementSnapshot(Base): __tablename__ = "creator_engagement_snapshots" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) creator_id = Column(UUID(as_uuid=True), ForeignKey("users.id", ondelete="CASCADE"), nullable=False) month = Column(DateTime, nullable=False) total_score = Column(Float, nullable=False) pool_share = Column(Float, nullable=True) payout_cents = Column(Integer, nullable=True) paid_at = Column(DateTime(timezone=True), nullable=True) created_at = Column(DateTime(timezone=True), default=datetime.utcnow)