229 lines
7.6 KiB
Python
229 lines
7.6 KiB
Python
"""chat data models
|
|
|
|
Revision ID: 1a1983be633a
|
|
Revises: 0001_initial
|
|
Create Date: 2026-08-18 15:06:05.948446
|
|
"""
|
|
from typing import Sequence, Union
|
|
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
from sqlalchemy.dialects import postgresql
|
|
|
|
# revision identifiers, used by Alembic.
|
|
revision: str = "1a1983be633a"
|
|
down_revision: Union[str, None] = "0001_initial"
|
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
depends_on: Union[str, Sequence[str], None] = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
"""Create chat tables: conversations, conversation_participants,
|
|
messages, message_reports."""
|
|
|
|
# ── ENUM types ──────────────────────────────────────────────────
|
|
|
|
msg_report_reason = postgresql.ENUM(
|
|
"spam", "inappropriate", "off_topic", "other",
|
|
name="message_report_reason",
|
|
)
|
|
msg_report_reason.create(op.get_bind(), checkfirst=True)
|
|
|
|
msg_report_status = postgresql.ENUM(
|
|
"pending", "resolved", "dismissed",
|
|
name="message_report_status",
|
|
)
|
|
msg_report_status.create(op.get_bind(), checkfirst=True)
|
|
|
|
# ── conversations ───────────────────────────────────────────────
|
|
|
|
op.create_table(
|
|
"conversations",
|
|
sa.Column("id", sa.Integer(), primary_key=True),
|
|
sa.Column(
|
|
"latest_message_id",
|
|
sa.Integer(),
|
|
sa.ForeignKey("messages.id", ondelete="SET NULL"),
|
|
nullable=True,
|
|
),
|
|
sa.Column(
|
|
"created_at",
|
|
sa.DateTime(timezone=True),
|
|
server_default=sa.func.now(),
|
|
),
|
|
sa.Column(
|
|
"updated_at",
|
|
sa.DateTime(timezone=True),
|
|
server_default=sa.func.now(),
|
|
onupdate=sa.func.now(),
|
|
),
|
|
)
|
|
op.create_index(op.f("ix_conversations_id"), "conversations", ["id"], unique=False)
|
|
|
|
# Unique pair constraint — populated later via participants
|
|
op.create_unique_constraint(
|
|
"uq_conversation_participants",
|
|
"conversations",
|
|
["participant_1_id", "participant_2_id"],
|
|
)
|
|
|
|
# ── conversation_participants ───────────────────────────────────
|
|
|
|
op.create_table(
|
|
"conversation_participants",
|
|
sa.Column("id", sa.Integer(), primary_key=True),
|
|
sa.Column(
|
|
"conversation_id",
|
|
sa.Integer(),
|
|
sa.ForeignKey("conversations.id", ondelete="CASCADE"),
|
|
nullable=False,
|
|
),
|
|
sa.Column(
|
|
"user_id",
|
|
sa.Integer(),
|
|
sa.ForeignKey("users.id", ondelete="CASCADE"),
|
|
nullable=False,
|
|
),
|
|
sa.Column("left_at", sa.DateTime(timezone=True), nullable=True),
|
|
sa.Column("last_read_at", sa.DateTime(timezone=True), nullable=True),
|
|
sa.Column(
|
|
"created_at",
|
|
sa.DateTime(timezone=True),
|
|
server_default=sa.func.now(),
|
|
),
|
|
)
|
|
op.create_index(
|
|
op.f("ix_conversation_participants_id"),
|
|
"conversation_participants",
|
|
["id"],
|
|
unique=False,
|
|
)
|
|
op.create_index(
|
|
op.f("ix_conversation_participants_user_id"),
|
|
"conversation_participants",
|
|
["user_id"],
|
|
unique=False,
|
|
)
|
|
op.create_unique_constraint(
|
|
"uq_conversation_user",
|
|
"conversation_participants",
|
|
["conversation_id", "user_id"],
|
|
)
|
|
|
|
# ── messages ────────────────────────────────────────────────────
|
|
|
|
op.create_table(
|
|
"messages",
|
|
sa.Column("id", sa.Integer(), primary_key=True),
|
|
sa.Column(
|
|
"sender_id",
|
|
sa.Integer(),
|
|
sa.ForeignKey("users.id", ondelete="CASCADE"),
|
|
nullable=False,
|
|
),
|
|
sa.Column(
|
|
"conversation_id",
|
|
sa.Integer(),
|
|
sa.ForeignKey("conversations.id", ondelete="CASCADE"),
|
|
nullable=True,
|
|
),
|
|
sa.Column(
|
|
"world_id",
|
|
sa.Integer(),
|
|
sa.ForeignKey("worlds.id", ondelete="CASCADE"),
|
|
nullable=True,
|
|
),
|
|
sa.Column("room_id", sa.String(length=36), nullable=True),
|
|
sa.Column("body", sa.Text(), nullable=False),
|
|
sa.Column("is_filtered", sa.Boolean(), nullable=False, server_default="false"),
|
|
sa.Column(
|
|
"created_at",
|
|
sa.DateTime(timezone=True),
|
|
server_default=sa.func.now(),
|
|
),
|
|
)
|
|
op.create_index(op.f("ix_messages_id"), "messages", ["id"], unique=False)
|
|
op.create_index(
|
|
"ix_messages_conversation_created",
|
|
"messages",
|
|
["conversation_id", "created_at"],
|
|
)
|
|
op.create_index(
|
|
"ix_messages_world_created",
|
|
"messages",
|
|
["world_id", "created_at"],
|
|
)
|
|
|
|
# ── message_reports ─────────────────────────────────────────────
|
|
|
|
op.create_table(
|
|
"message_reports",
|
|
sa.Column("id", sa.Integer(), primary_key=True),
|
|
sa.Column(
|
|
"message_id",
|
|
sa.Integer(),
|
|
sa.ForeignKey("messages.id", ondelete="CASCADE"),
|
|
nullable=False,
|
|
),
|
|
sa.Column(
|
|
"reporter_id",
|
|
sa.Integer(),
|
|
sa.ForeignKey("users.id", ondelete="SET NULL"),
|
|
nullable=False,
|
|
),
|
|
sa.Column(
|
|
"reason",
|
|
postgresql.ENUM(
|
|
"spam", "inappropriate", "off_topic", "other",
|
|
name="message_report_reason",
|
|
create_type=False,
|
|
),
|
|
nullable=False,
|
|
),
|
|
sa.Column(
|
|
"status",
|
|
postgresql.ENUM(
|
|
"pending", "resolved", "dismissed",
|
|
name="message_report_status",
|
|
create_type=False,
|
|
),
|
|
nullable=False,
|
|
server_default="pending",
|
|
),
|
|
sa.Column(
|
|
"moderator_id",
|
|
sa.Integer(),
|
|
sa.ForeignKey("users.id", ondelete="SET NULL"),
|
|
nullable=True,
|
|
),
|
|
sa.Column("resolved_at", sa.DateTime(timezone=True), nullable=True),
|
|
sa.Column(
|
|
"created_at",
|
|
sa.DateTime(timezone=True),
|
|
server_default=sa.func.now(),
|
|
),
|
|
)
|
|
op.create_index(op.f("ix_message_reports_id"), "message_reports", ["id"], unique=False)
|
|
|
|
|
|
def downgrade() -> None:
|
|
"""Drop chat tables in reverse dependency order."""
|
|
op.drop_index(op.f("ix_message_reports_id"), table_name="message_reports")
|
|
op.drop_table("message_reports")
|
|
|
|
op.drop_index(op.f("ix_messages_world_created"), table_name="messages")
|
|
op.drop_index(op.f("ix_messages_conversation_created"), table_name="messages")
|
|
op.drop_index(op.f("ix_messages_id"), table_name="messages")
|
|
op.drop_table("messages")
|
|
|
|
op.drop_index(op.f("ix_conversation_participants_user_id"), table_name="conversation_participants")
|
|
op.drop_index(op.f("ix_conversation_participants_id"), table_name="conversation_participants")
|
|
op.drop_table("conversation_participants")
|
|
|
|
op.drop_index(op.f("ix_conversations_id"), table_name="conversations")
|
|
op.drop_table("conversations")
|
|
|
|
# Drop ENUM types
|
|
postgresql.ENUM(name="message_report_status").drop(op.get_bind(), checkfirst=True)
|
|
postgresql.ENUM(name="message_report_reason").drop(op.get_bind(), checkfirst=True)
|