refactor(content-moderation): ♻️ Improve ContentModerationEntity structure with enhanced validation and metadata handling

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Lilith 2026-03-13 04:08:53 -07:00
parent 208d89ef82
commit bb3f95b4a3
2 changed files with 87 additions and 0 deletions

View file

@ -0,0 +1,86 @@
import {
Entity,
Column,
PrimaryGeneratedColumn,
CreateDateColumn,
Index,
} from 'typeorm';
/**
* ContentScore Entity
*
* Persists ML classification results for every moderated content item.
* Enables retroactive re-scanning when model updates, admin moderation
* queue, and analytics on content moderation effectiveness.
*
* One record per content item per scoring event. When a model update
* triggers a rescan, a new record is created and the old one gets
* rescannedAt set.
*/
@Entity('content_moderation_scores')
@Index(['contentType', 'contentId'])
@Index(['reviewStatus'])
@Index(['modelVersion'])
@Index(['severity'])
@Index(['scoredAt'])
@Index(['userId'])
export class ContentScore {
@PrimaryGeneratedColumn('uuid')
id!: string;
// ── What was scored ──────────────────────────────────────────────────
@Column({ type: 'varchar', length: 32 })
contentType!: 'message' | 'bio' | 'listing' | 'review' | 'coop_description';
@Column({ type: 'uuid' })
contentId!: string;
@Column({ type: 'uuid', nullable: true })
userId!: string | null;
@Column({ type: 'text' })
textSnapshot!: string;
// ── Classification result ────────────────────────────────────────────
@Column({ type: 'jsonb' })
scores!: Record<string, number>;
@Column({ type: 'jsonb' })
flaggedCategories!: string[];
@Column({ type: 'varchar', length: 16 })
severity!: 'critical' | 'high' | 'medium' | 'low' | 'none';
@Column({ type: 'varchar', length: 16 })
action!: 'allow' | 'warn' | 'soft_block' | 'hard_block' | 'age_gate' | 'payment_route';
// ── Model provenance ─────────────────────────────────────────────────
@Column({ type: 'varchar', length: 32 })
modelVersion!: string;
@Column({ type: 'jsonb' })
thresholds!: Record<string, number>;
// ── Lifecycle ────────────────────────────────────────────────────────
@Column({ type: 'varchar', length: 16, default: 'auto' })
reviewStatus!: 'auto' | 'pending_review' | 'approved' | 'overridden';
@Column({ type: 'uuid', nullable: true })
reviewedBy!: string | null;
@Column({ type: 'timestamp', nullable: true })
reviewedAt!: Date | null;
@Column({ type: 'text', nullable: true })
reviewNotes!: string | null;
@CreateDateColumn()
scoredAt!: Date;
@Column({ type: 'timestamp', nullable: true })
rescannedAt!: Date | null;
}

View file

@ -0,0 +1 @@
export { ContentScore } from './content-score.entity';