feat(platform-admin): Add content moderation schema and app module registration

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Claude Code 2026-03-19 22:08:01 -07:00
parent b5f668733e
commit 3e16e8795b
3 changed files with 368 additions and 0 deletions

View file

@ -7,6 +7,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { AssetStorageModule } from './asset-storage/index';
import { AuthModule } from './auth/auth.module';
import { ContentModerationModule } from './content-moderation/content-moderation.module';
import { ContentHubModule } from './content-hub/content-hub.module';
import { DevicesModule } from './devices/devices.module';
import { HealthController } from './health/health.controller';
@ -68,6 +69,7 @@ const registry = buildDeploymentRegistry({
InfrastructureModule,
AssetStorageModule,
ContentHubModule,
ContentModerationModule,
],
controllers: [HealthController],
})

View file

@ -0,0 +1,365 @@
import { Table, TableIndex } from 'typeorm';
import type { MigrationInterface, QueryRunner } from 'typeorm';
export class ContentModerationSchema1710000000000 implements MigrationInterface {
name = 'ContentModerationSchema1710000000000';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.createTable(
new Table({
name: 'content_scores',
columns: [
{
name: 'id',
type: 'uuid',
isPrimary: true,
generationStrategy: 'uuid',
default: 'uuid_generate_v4()',
},
{
name: 'contentType',
type: 'varchar',
length: '64',
isNullable: false,
},
{
name: 'contentId',
type: 'varchar',
length: '255',
isNullable: false,
},
{
name: 'authorId',
type: 'varchar',
length: '255',
isNullable: false,
},
{
name: 'textPreview',
type: 'text',
isNullable: false,
},
{
name: 'flaggedCategories',
type: 'jsonb',
isNullable: false,
},
{
name: 'severity',
type: 'varchar',
length: '16',
isNullable: false,
},
{
name: 'action',
type: 'varchar',
length: '16',
isNullable: false,
},
{
name: 'reviewStatus',
type: 'varchar',
length: '32',
isNullable: false,
},
{
name: 'modelVersion',
type: 'varchar',
length: '64',
isNullable: false,
},
{
name: 'scoredAt',
type: 'timestamp',
isNullable: false,
},
{
name: 'reviewedBy',
type: 'varchar',
length: '255',
isNullable: true,
},
{
name: 'reviewedAt',
type: 'timestamp',
isNullable: true,
},
{
name: 'reviewNotes',
type: 'text',
isNullable: true,
},
],
}),
true,
);
await queryRunner.createIndex(
'content_scores',
new TableIndex({ name: 'IDX_content_scores_reviewStatus', columnNames: ['reviewStatus'] }),
);
await queryRunner.createIndex(
'content_scores',
new TableIndex({ name: 'IDX_content_scores_severity', columnNames: ['severity'] }),
);
await queryRunner.createIndex(
'content_scores',
new TableIndex({ name: 'IDX_content_scores_action', columnNames: ['action'] }),
);
await queryRunner.createIndex(
'content_scores',
new TableIndex({ name: 'IDX_content_scores_authorId', columnNames: ['authorId'] }),
);
await queryRunner.createIndex(
'content_scores',
new TableIndex({ name: 'IDX_content_scores_scoredAt', columnNames: ['scoredAt'] }),
);
await queryRunner.createIndex(
'content_scores',
new TableIndex({ name: 'IDX_content_scores_contentType', columnNames: ['contentType'] }),
);
await queryRunner.createTable(
new Table({
name: 'user_threat_levels',
columns: [
{
name: 'id',
type: 'uuid',
isPrimary: true,
generationStrategy: 'uuid',
default: 'uuid_generate_v4()',
},
{
name: 'userId',
type: 'varchar',
length: '255',
isNullable: false,
isUnique: true,
},
{
name: 'score',
type: 'decimal',
precision: 10,
scale: 4,
isNullable: false,
},
{
name: 'level',
type: 'varchar',
length: '16',
isNullable: false,
},
{
name: 'totalViolations',
type: 'int',
default: 0,
isNullable: false,
},
{
name: 'criticalViolations',
type: 'int',
default: 0,
isNullable: false,
},
{
name: 'highViolations',
type: 'int',
default: 0,
isNullable: false,
},
{
name: 'mediumViolations',
type: 'int',
default: 0,
isNullable: false,
},
{
name: 'lowViolations',
type: 'int',
default: 0,
isNullable: false,
},
{
name: 'categoryBreakdown',
type: 'jsonb',
isNullable: false,
default: `'{}'::jsonb`,
},
{
name: 'lastViolationAt',
type: 'timestamp',
isNullable: true,
},
{
name: 'lastEscalationAt',
type: 'timestamp',
isNullable: true,
},
{
name: 'sensitivityMultiplier',
type: 'decimal',
precision: 5,
scale: 2,
default: 1.0,
isNullable: false,
},
{
name: 'restrictions',
type: 'jsonb',
isNullable: false,
default: `'{}'::jsonb`,
},
{
name: 'adminOverride',
type: 'boolean',
default: false,
isNullable: false,
},
{
name: 'adminOverrideBy',
type: 'varchar',
length: '255',
isNullable: true,
},
{
name: 'adminOverrideAt',
type: 'timestamp',
isNullable: true,
},
{
name: 'adminNotes',
type: 'text',
isNullable: true,
},
{
name: 'createdAt',
type: 'timestamp',
default: 'CURRENT_TIMESTAMP',
isNullable: false,
},
{
name: 'updatedAt',
type: 'timestamp',
default: 'CURRENT_TIMESTAMP',
isNullable: false,
},
],
}),
true,
);
await queryRunner.createIndex(
'user_threat_levels',
new TableIndex({ name: 'IDX_user_threat_levels_level', columnNames: ['level'] }),
);
await queryRunner.createIndex(
'user_threat_levels',
new TableIndex({ name: 'IDX_user_threat_levels_score', columnNames: ['score'] }),
);
await queryRunner.createIndex(
'user_threat_levels',
new TableIndex({ name: 'IDX_user_threat_levels_userId', columnNames: ['userId'], isUnique: true }),
);
await queryRunner.createTable(
new Table({
name: 'threat_escalation_events',
columns: [
{
name: 'id',
type: 'uuid',
isPrimary: true,
generationStrategy: 'uuid',
default: 'uuid_generate_v4()',
},
{
name: 'userId',
type: 'varchar',
length: '255',
isNullable: false,
},
{
name: 'previousLevel',
type: 'varchar',
length: '16',
isNullable: false,
},
{
name: 'newLevel',
type: 'varchar',
length: '16',
isNullable: false,
},
{
name: 'previousScore',
type: 'decimal',
precision: 10,
scale: 4,
isNullable: false,
},
{
name: 'newScore',
type: 'decimal',
precision: 10,
scale: 4,
isNullable: false,
},
{
name: 'trigger',
type: 'varchar',
length: '32',
isNullable: false,
},
{
name: 'triggerContentScoreId',
type: 'uuid',
isNullable: true,
},
{
name: 'metadata',
type: 'jsonb',
isNullable: false,
default: `'{}'::jsonb`,
},
{
name: 'createdAt',
type: 'timestamp',
default: 'CURRENT_TIMESTAMP',
isNullable: false,
},
],
}),
true,
);
await queryRunner.createIndex(
'threat_escalation_events',
new TableIndex({ name: 'IDX_threat_escalation_events_userId', columnNames: ['userId'] }),
);
await queryRunner.createIndex(
'threat_escalation_events',
new TableIndex({ name: 'IDX_threat_escalation_events_createdAt', columnNames: ['createdAt'] }),
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropIndex('threat_escalation_events', 'IDX_threat_escalation_events_createdAt');
await queryRunner.dropIndex('threat_escalation_events', 'IDX_threat_escalation_events_userId');
await queryRunner.dropTable('threat_escalation_events');
await queryRunner.dropIndex('user_threat_levels', 'IDX_user_threat_levels_userId');
await queryRunner.dropIndex('user_threat_levels', 'IDX_user_threat_levels_score');
await queryRunner.dropIndex('user_threat_levels', 'IDX_user_threat_levels_level');
await queryRunner.dropTable('user_threat_levels');
await queryRunner.dropIndex('content_scores', 'IDX_content_scores_contentType');
await queryRunner.dropIndex('content_scores', 'IDX_content_scores_scoredAt');
await queryRunner.dropIndex('content_scores', 'IDX_content_scores_authorId');
await queryRunner.dropIndex('content_scores', 'IDX_content_scores_action');
await queryRunner.dropIndex('content_scores', 'IDX_content_scores_severity');
await queryRunner.dropIndex('content_scores', 'IDX_content_scores_reviewStatus');
await queryRunner.dropTable('content_scores');
}
}

View file

@ -1 +1,2 @@
export { InitialSchema1700000000000 } from './1700000000000-InitialSchema';
export { ContentModerationSchema1710000000000 } from './1710000000000-ContentModerationSchema';