"""add fediverse social tables Revision ID: l2j0g6h8i9 Revises: k1i9f5g7h8 Create Date: 2026-02-22 Creates: - ap_remote_actors — cached profiles of remote actors - ap_following — outbound follows (local → remote) - ap_remote_posts — ingested posts from remote actors - ap_local_posts — native posts composed in federation UI - ap_interactions — likes and boosts - ap_notifications — follow/like/boost/mention/reply notifications """ from alembic import op import sqlalchemy as sa from sqlalchemy.dialects.postgresql import JSONB revision = "l2j0g6h8i9" down_revision = "k1i9f5g7h8" branch_labels = None depends_on = None def upgrade() -> None: # -- ap_remote_actors -- op.create_table( "ap_remote_actors", sa.Column("id", sa.Integer, primary_key=True, autoincrement=True), sa.Column("actor_url", sa.String(512), unique=True, nullable=False), sa.Column("inbox_url", sa.String(512), nullable=False), sa.Column("shared_inbox_url", sa.String(512), nullable=True), sa.Column("preferred_username", sa.String(255), nullable=False), sa.Column("display_name", sa.String(255), nullable=True), sa.Column("summary", sa.Text, nullable=True), sa.Column("icon_url", sa.String(512), nullable=True), sa.Column("public_key_pem", sa.Text, nullable=True), sa.Column("domain", sa.String(255), nullable=False), sa.Column("fetched_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), ) op.create_index("ix_ap_remote_actor_url", "ap_remote_actors", ["actor_url"], unique=True) op.create_index("ix_ap_remote_actor_domain", "ap_remote_actors", ["domain"]) # -- ap_following -- op.create_table( "ap_following", sa.Column("id", sa.Integer, primary_key=True, autoincrement=True), sa.Column("actor_profile_id", sa.Integer, sa.ForeignKey("ap_actor_profiles.id", ondelete="CASCADE"), nullable=False), sa.Column("remote_actor_id", sa.Integer, sa.ForeignKey("ap_remote_actors.id", ondelete="CASCADE"), nullable=False), sa.Column("state", sa.String(20), nullable=False, server_default="pending"), sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), sa.Column("accepted_at", sa.DateTime(timezone=True), nullable=True), sa.UniqueConstraint("actor_profile_id", "remote_actor_id", name="uq_following"), ) op.create_index("ix_ap_following_actor", "ap_following", ["actor_profile_id"]) op.create_index("ix_ap_following_remote", "ap_following", ["remote_actor_id"]) # -- ap_remote_posts -- op.create_table( "ap_remote_posts", sa.Column("id", sa.Integer, primary_key=True, autoincrement=True), sa.Column("remote_actor_id", sa.Integer, sa.ForeignKey("ap_remote_actors.id", ondelete="CASCADE"), nullable=False), sa.Column("activity_id", sa.String(512), unique=True, nullable=False), sa.Column("object_id", sa.String(512), unique=True, nullable=False), sa.Column("object_type", sa.String(64), nullable=False, server_default="Note"), sa.Column("content", sa.Text, nullable=True), sa.Column("summary", sa.Text, nullable=True), sa.Column("url", sa.String(512), nullable=True), sa.Column("attachment_data", JSONB, nullable=True), sa.Column("tag_data", JSONB, nullable=True), sa.Column("in_reply_to", sa.String(512), nullable=True), sa.Column("conversation", sa.String(512), nullable=True), sa.Column("published", sa.DateTime(timezone=True), nullable=True), sa.Column("fetched_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), ) op.create_index("ix_ap_remote_post_actor", "ap_remote_posts", ["remote_actor_id"]) op.create_index("ix_ap_remote_post_published", "ap_remote_posts", ["published"]) op.create_index("ix_ap_remote_post_object", "ap_remote_posts", ["object_id"], unique=True) # -- ap_local_posts -- op.create_table( "ap_local_posts", sa.Column("id", sa.Integer, primary_key=True, autoincrement=True), sa.Column("actor_profile_id", sa.Integer, sa.ForeignKey("ap_actor_profiles.id", ondelete="CASCADE"), nullable=False), sa.Column("content", sa.Text, nullable=False), sa.Column("visibility", sa.String(20), nullable=False, server_default="public"), sa.Column("in_reply_to", sa.String(512), nullable=True), sa.Column("published", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), ) op.create_index("ix_ap_local_post_actor", "ap_local_posts", ["actor_profile_id"]) op.create_index("ix_ap_local_post_published", "ap_local_posts", ["published"]) # -- ap_interactions -- op.create_table( "ap_interactions", sa.Column("id", sa.Integer, primary_key=True, autoincrement=True), sa.Column("actor_profile_id", sa.Integer, sa.ForeignKey("ap_actor_profiles.id", ondelete="CASCADE"), nullable=True), sa.Column("remote_actor_id", sa.Integer, sa.ForeignKey("ap_remote_actors.id", ondelete="CASCADE"), nullable=True), sa.Column("post_type", sa.String(20), nullable=False), sa.Column("post_id", sa.Integer, nullable=False), sa.Column("interaction_type", sa.String(20), nullable=False), sa.Column("activity_id", sa.String(512), nullable=True), sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), ) op.create_index("ix_ap_interaction_post", "ap_interactions", ["post_type", "post_id"]) op.create_index("ix_ap_interaction_actor", "ap_interactions", ["actor_profile_id"]) op.create_index("ix_ap_interaction_remote", "ap_interactions", ["remote_actor_id"]) # -- ap_notifications -- op.create_table( "ap_notifications", sa.Column("id", sa.Integer, primary_key=True, autoincrement=True), sa.Column("actor_profile_id", sa.Integer, sa.ForeignKey("ap_actor_profiles.id", ondelete="CASCADE"), nullable=False), sa.Column("notification_type", sa.String(20), nullable=False), sa.Column("from_remote_actor_id", sa.Integer, sa.ForeignKey("ap_remote_actors.id", ondelete="SET NULL"), nullable=True), sa.Column("from_actor_profile_id", sa.Integer, sa.ForeignKey("ap_actor_profiles.id", ondelete="SET NULL"), nullable=True), sa.Column("target_activity_id", sa.Integer, sa.ForeignKey("ap_activities.id", ondelete="SET NULL"), nullable=True), sa.Column("target_remote_post_id", sa.Integer, sa.ForeignKey("ap_remote_posts.id", ondelete="SET NULL"), nullable=True), sa.Column("read", sa.Boolean, nullable=False, server_default="false"), sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False), ) op.create_index("ix_ap_notification_actor", "ap_notifications", ["actor_profile_id"]) op.create_index("ix_ap_notification_read", "ap_notifications", ["actor_profile_id", "read"]) op.create_index("ix_ap_notification_created", "ap_notifications", ["created_at"]) def downgrade() -> None: op.drop_table("ap_notifications") op.drop_table("ap_interactions") op.drop_table("ap_local_posts") op.drop_table("ap_remote_posts") op.drop_table("ap_following") op.drop_table("ap_remote_actors")