From 1dacc2a68462dbfc23a95d3eac6f5277fa1489f8 Mon Sep 17 00:00:00 2001 From: coder-bot Date: Tue, 18 Aug 2026 18:48:01 +0000 Subject: [PATCH] WIP checkpoint: Trust & Safety: Moderation data model + Alembic migration --- backend/alembic/versions/0003_moderation.py | 35 +++++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/backend/alembic/versions/0003_moderation.py b/backend/alembic/versions/0003_moderation.py index 0138640..cddd073 100644 --- a/backend/alembic/versions/0003_moderation.py +++ b/backend/alembic/versions/0003_moderation.py @@ -18,16 +18,40 @@ branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None +def _create_enum_if_not_exists(enum_name: str, values: list[str]) -> None: + """Create a PostgreSQL ENUM type if it does not already exist.""" + bind = op.get_bind() + # Check if the type already exists in pg_type + result = bind.execute( + sa.text( + "SELECT 1 FROM pg_type WHERE typname = :name" + ).bindparams(name=enum_name) + ).fetchone() + if result is None: + values_str = ", ".join(f"'{v}'" for v in values) + op.execute(f"CREATE TYPE {enum_name} AS ENUM ({values_str})") + + def upgrade() -> None: """Add role, banned_until, suspended_until to users table and create reports, moderation_actions, appeals tables.""" + # --- Create ENUM types if they don't exist --- + _create_enum_if_not_exists("user_role", ["user", "moderator", "admin"]) + _create_enum_if_not_exists("report_target_type", ["user", "world", "asset", "chat_message"]) + _create_enum_if_not_exists("report_reason_category", [ + "harassment", "hate_speech", "sexual_content", + "violence", "spam", "self_harm", "other" + ]) + _create_enum_if_not_exists("report_status", ["open", "in_review", "resolved", "dismissed"]) + _create_enum_if_not_exists("report_resolution", ["warn", "suspend", "ban", "restrict", "remove"]) + _create_enum_if_not_exists("moderation_target_type", ["user", "world", "asset", "chat_message"]) + _create_enum_if_not_exists("moderation_action_type", ["warn", "suspend", "ban", "restrict", "remove", "restore"]) + _create_enum_if_not_exists("appeal_status", ["pending", "approved", "rejected"]) + # --- Extend users table --- - # Add role column as ENUM - op.execute( - "CREATE TYPE user_role AS ENUM ('user', 'moderator', 'admin')" - ) + # Add role column op.add_column( "users", sa.Column( @@ -176,9 +200,8 @@ def downgrade() -> None: op.drop_column("users", "banned_until") op.drop_column("users", "role") - # Drop the user_role enum type + # Drop the enum types op.execute("DROP TYPE IF EXISTS user_role") - # Drop other enum types created op.execute("DROP TYPE IF EXISTS report_target_type") op.execute("DROP TYPE IF EXISTS report_reason_category") op.execute("DROP TYPE IF EXISTS report_status")