WIP checkpoint: Trust & Safety: Moderation data model + Alembic migration

This commit is contained in:
2026-08-18 18:36:27 +00:00
parent 9c6390e73b
commit ae498ce4cb
2 changed files with 62 additions and 18 deletions
+60
View File
@@ -0,0 +1,60 @@
"""Report model for Trust & Safety."""
from sqlalchemy import (
Column,
Integer,
String,
Text,
DateTime,
ForeignKey,
Enum as SAEnum,
)
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from app.database import Base
class Report(Base):
__tablename__ = "reports"
id = Column(Integer, primary_key=True)
reporter_id = Column(Integer, ForeignKey("users.id"), nullable=False)
target_type = SAEnum("user", "world", "asset", "chat_message", name="target_type_enum", nullable=False)
target_id = Column(String(64), nullable=False)
reason_category = SAEnum(
"harassment",
"hate_speech",
"sexual_content",
"violence",
"spam",
"self_harm",
"other",
name="reason_category_enum",
nullable=False,
)
details = Column(Text, nullable=True)
status = SAEnum(
"open",
"in_review",
"resolved",
"dismissed",
name="report_status_enum",
default="open",
nullable=False,
)
resolved_by = Column(Integer, ForeignKey("users.id"), nullable=True)
resolution = SAEnum(
"action_taken",
"action_not_needed",
"duplicate",
name="resolution_enum",
nullable=True,
)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
resolved_at = Column(DateTime(timezone=True), nullable=True)
# Relationships
reporter = relationship("User", foreign_keys=[reporter_id])
resolved_by_user = relationship("User", foreign_keys=[resolved_by])
+2 -18
View File
@@ -1,23 +1,13 @@
from datetime import date
from typing import Optional
from sqlalchemy import Column, Integer, String, Boolean, DateTime, Date, Enum
from sqlalchemy import Column, Integer, String, Boolean, DateTime, Date, Enum, Text, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from app.database import Base
class UserRole:
USER = "user"
MODERATOR = "moderator"
ADMIN = "admin"
@staticmethod
def choices():
return [UserRole.USER, UserRole.MODERATOR, UserRole.ADMIN]
class User(Base):
__tablename__ = "users"
@@ -27,7 +17,7 @@ class User(Base):
hashed_password = Column(String(255), nullable=False)
date_of_birth = Column(Date, nullable=True)
is_active = Column(Boolean, default=True)
role = Column(Enum(UserRole.USER, UserRole.MODERATOR, UserRole.ADMIN), default=UserRole.USER, nullable=False)
role = Column(Enum("user", "moderator", "admin", name="user_role", default="user"), default="user")
banned_until = Column(DateTime(timezone=True), nullable=True)
suspended_until = Column(DateTime(timezone=True), nullable=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
@@ -36,12 +26,6 @@ class User(Base):
sessions = relationship("Session", back_populates="user", cascade="all, delete-orphan")
worlds = relationship("World", back_populates="owner")
assets = relationship("Asset", back_populates="owner")
reports_made = relationship("Report", back_populates="reporter", foreign_keys="Report.reporter_id")
reports_resolved = relationship("Report", back_populates="resolved_by_user", foreign_keys="Report.resolved_by")
moderation_actions_created = relationship("ModerationAction", back_populates="moderator", foreign_keys="ModerationAction.moderator_id")
moderation_actions_target = relationship("ModerationAction", back_populates="target_user", foreign_keys="ModerationAction.target_user_id")
appeals = relationship("Appeal", back_populates="user", foreign_keys="Appeal.user_id")
appeals_reviewed = relationship("Appeal", back_populates="reviewer", foreign_keys="Appeal.reviewed_by")
@property
def is_adult(self) -> bool: