"""sx-pub ORM models — federated SX publishing protocol. Tables for the sx-pub actor, content collections, published documents, outbox activities, and federation relationships (followers/following). """ from __future__ import annotations from datetime import datetime from sqlalchemy import ( String, Integer, DateTime, Text, ForeignKey, UniqueConstraint, Index, func, ) from sqlalchemy.dialects.postgresql import JSONB from sqlalchemy.orm import Mapped, mapped_column, relationship from shared.db.base import Base class SxPubActor(Base): """Singleton actor for this sx-pub instance.""" __tablename__ = "sx_pub_actor" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) preferred_username: Mapped[str] = mapped_column(String(64), unique=True, nullable=False) display_name: Mapped[str | None] = mapped_column(String(255), nullable=True) summary: Mapped[str | None] = mapped_column(Text, nullable=True) public_key_pem: Mapped[str] = mapped_column(Text, nullable=False) private_key_pem: Mapped[str] = mapped_column(Text, nullable=False) domain: Mapped[str] = mapped_column(String(255), nullable=False) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now(), ) def __repr__(self) -> str: return f"" class SxPubCollection(Base): """Named grouping of published documents (e.g. core-specs, platforms).""" __tablename__ = "sx_pub_collections" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) slug: Mapped[str] = mapped_column(String(128), unique=True, nullable=False) name: Mapped[str] = mapped_column(String(255), nullable=False) description: Mapped[str | None] = mapped_column(Text, nullable=True) sort_order: Mapped[int] = mapped_column(Integer, nullable=False, default=0, server_default="0") created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now(), ) documents = relationship("SxPubDocument", back_populates="collection", lazy="selectin") def __repr__(self) -> str: return f"" class SxPubDocument(Base): """Published content — path→CID index entry.""" __tablename__ = "sx_pub_documents" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) collection_id: Mapped[int] = mapped_column( Integer, ForeignKey("sx_pub_collections.id", ondelete="CASCADE"), nullable=False, ) slug: Mapped[str] = mapped_column(String(255), nullable=False) title: Mapped[str | None] = mapped_column(String(512), nullable=True) summary: Mapped[str | None] = mapped_column(Text, nullable=True) content_hash: Mapped[str] = mapped_column(String(128), nullable=False) ipfs_cid: Mapped[str | None] = mapped_column(String(128), nullable=True) size_bytes: Mapped[int | None] = mapped_column(Integer, nullable=True) requires: Mapped[dict | None] = mapped_column(JSONB, nullable=True) status: Mapped[str] = mapped_column( String(20), nullable=False, default="draft", server_default="draft", ) published_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now(), ) collection = relationship("SxPubCollection", back_populates="documents") __table_args__ = ( UniqueConstraint("collection_id", "slug", name="uq_pub_doc_collection_slug"), Index("ix_pub_doc_collection", "collection_id"), Index("ix_pub_doc_status", "status"), Index("ix_pub_doc_cid", "ipfs_cid"), ) def __repr__(self) -> str: return f"" class SxPubActivity(Base): """Outbox activity (Publish, Follow, Announce, Anchor).""" __tablename__ = "sx_pub_activities" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) activity_type: Mapped[str] = mapped_column(String(64), nullable=False) object_type: Mapped[str | None] = mapped_column(String(64), nullable=True) object_data: Mapped[dict | None] = mapped_column(JSONB, nullable=True) ipfs_cid: Mapped[str | None] = mapped_column(String(128), nullable=True) published: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now(), ) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now(), ) __table_args__ = ( Index("ix_pub_activity_type", "activity_type"), Index("ix_pub_activity_published", "published"), ) def __repr__(self) -> str: return f"" class SxPubFollower(Base): """Remote server that follows us.""" __tablename__ = "sx_pub_followers" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) follower_acct: Mapped[str] = mapped_column(String(512), nullable=False) follower_inbox: Mapped[str] = mapped_column(String(512), nullable=False) follower_actor_url: Mapped[str] = mapped_column(String(512), nullable=False) follower_public_key: Mapped[str | None] = mapped_column(Text, nullable=True) state: Mapped[str] = mapped_column( String(20), nullable=False, default="accepted", server_default="accepted", ) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now(), ) __table_args__ = ( UniqueConstraint("follower_acct", name="uq_pub_follower_acct"), Index("ix_pub_follower_state", "state"), ) def __repr__(self) -> str: return f"" class SxPubFollowing(Base): """Remote server we follow.""" __tablename__ = "sx_pub_following" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) remote_actor_url: Mapped[str] = mapped_column(String(512), unique=True, nullable=False) remote_inbox: Mapped[str] = mapped_column(String(512), nullable=False) state: Mapped[str] = mapped_column( String(20), nullable=False, default="pending", server_default="pending", ) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), nullable=False, server_default=func.now(), ) accepted_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) __table_args__ = ( Index("ix_pub_following_state", "state"), ) def __repr__(self) -> str: return f""