from datetime import datetime from typing import Optional from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime from sqlalchemy.dialects.postgresql import ENUM from sqlalchemy.orm import relationship from sqlalchemy.sql import func import enum from app.database import Base class TargetType(str, enum.Enum): USER = "user" WORLD = "world" ASSET = "asset" CHAT_MESSAGE = "chat_message" class ReasonCategory(str, enum.Enum): HARASSMENT = "harassment" HATE_SPEECH = "hate_speech" SEXUAL_CONTENT = "sexual_content" VIOLENCE = "violence" SPAM = "spam" SELF_HARM = "self_harm" OTHER = "other" class ReportStatus(str, enum.Enum): OPEN = "open" IN_REVIEW = "in_review" RESOLVED = "resolved" DISMISSED = "dismissed" class Resolution(str, enum.Enum): ACTION_TAKEN = "action_taken" DISMISSED = "dismissed" NO_VIOLATION = "no_violation" class ActionType(str, enum.Enum): WARN = "warn" SUSPEND = "suspend" BAN = "ban" RESTRICT = "restrict" REMOVE = "remove" RESTORE = "restore" class AppealStatus(str, enum.Enum): PENDING = "pending" APPROVED = "approved" REJECTED = "rejected" class Report(Base): __tablename__ = "reports" id = Column(Integer, primary_key=True, index=True) reporter_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False) target_type = Column( ENUM(TargetType, name="target_type", create_type=False), nullable=False, ) target_id = Column(String(64), nullable=False) reason_category = Column( ENUM(ReasonCategory, name="reason_category", create_type=False), nullable=False, ) details = Column(Text, nullable=True) status = Column( ENUM(ReportStatus, name="report_status", create_type=False), default=ReportStatus.OPEN, nullable=False, ) resolved_by = Column(Integer, ForeignKey("users.id", ondelete="SET NULL"), nullable=True) resolution = Column( ENUM(Resolution, name="resolution", create_type=False), nullable=True, ) resolved_at = Column(DateTime(timezone=True), nullable=True) created_at = Column(DateTime(timezone=True), server_default=func.now()) updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) reporter = relationship("User", foreign_keys=[reporter_id]) resolved_by_user = relationship("User", foreign_keys=[resolved_by]) moderation_actions = relationship("ModerationAction", back_populates="report") class ModerationAction(Base): __tablename__ = "moderation_actions" id = Column(Integer, primary_key=True, index=True) moderator_id = Column(Integer, ForeignKey("users.id", ondelete="SET NULL"), nullable=False) target_user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False) target_type = Column( ENUM(TargetType, name="target_type", create_type=False), nullable=False, ) target_id = Column(String(64), nullable=False) action = Column( ENUM(ActionType, name="action_type", create_type=False), nullable=False, ) reason = Column(Text, nullable=True) report_id = Column(Integer, ForeignKey("reports.id", ondelete="SET NULL"), nullable=True) expires_at = Column(DateTime(timezone=True), nullable=True) revoked_at = Column(DateTime(timezone=True), nullable=True) created_at = Column(DateTime(timezone=True), server_default=func.now()) moderator = relationship("User", foreign_keys=[moderator_id]) target_user = relationship("User", foreign_keys=[target_user_id]) report = relationship("Report", back_populates="moderation_actions") appeals = relationship("Appeal", back_populates="moderation_action") class Appeal(Base): __tablename__ = "appeals" id = Column(Integer, primary_key=True, index=True) moderation_action_id = Column(Integer, ForeignKey("moderation_actions.id", ondelete="CASCADE"), nullable=False) user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False) reason = Column(Text, nullable=False) status = Column( ENUM(AppealStatus, name="appeal_status", create_type=False), default=AppealStatus.PENDING, nullable=False, ) reviewed_by = Column(Integer, ForeignKey("users.id", ondelete="SET NULL"), nullable=True) reviewed_at = Column(DateTime(timezone=True), nullable=True) created_at = Column(DateTime(timezone=True), server_default=func.now()) moderation_action = relationship("ModerationAction", back_populates="appeals") user = relationship("User", foreign_keys=[user_id]) reviewed_by_user = relationship("User", foreign_keys=[reviewed_by])