- Rename EngagementEvent.metadata → event_metadata (SQLAlchemy reserved name) - Replace passlib with direct bcrypt usage (passlib incompatible with bcrypt 5.0) - Fix renderer Dockerfile: npm ci → npm install (no lockfile) - Fix frontend Dockerfile: single-stage, skip tsc for builds - Remove deprecated 'version' key from docker-compose.yml - Add docker-compose.dev.yml for data-stores-only local dev - Add start_period to API healthcheck for startup grace
222 lines
9.9 KiB
Python
222 lines
9.9 KiB
Python
"""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)
|
|
event_metadata = Column("metadata", 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)
|