diff --git a/packages/apps/human-app/server/src/modules/abuse/model/abuse.model.ts b/packages/apps/human-app/server/src/modules/abuse/model/abuse.model.ts index 37eec9942a..fda6f2e6b3 100644 --- a/packages/apps/human-app/server/src/modules/abuse/model/abuse.model.ts +++ b/packages/apps/human-app/server/src/modules/abuse/model/abuse.model.ts @@ -1,7 +1,12 @@ import { AutoMap } from '@automapper/classes'; import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; -import { IsEthereumAddress, IsNumber } from 'class-validator'; +import { + IsEthereumAddress, + IsNotEmpty, + IsNumber, + IsString, +} from 'class-validator'; export class ReportAbuseDto { @AutoMap() @@ -13,6 +18,11 @@ export class ReportAbuseDto { @Type(() => Number) @ApiProperty() chain_id: number; + @AutoMap() + @IsString() + @IsNotEmpty() + @ApiProperty({ required: true, description: 'Reason for the abuse report' }) + reason: string; } export class ReportAbuseParams { @@ -20,7 +30,10 @@ export class ReportAbuseParams { chainId: number; @AutoMap() escrowAddress: string; + @AutoMap() + reason: string; } + export class ReportAbuseCommand { @AutoMap() data: ReportAbuseParams; @@ -33,6 +46,8 @@ export class ReportAbuseData { escrow_address: string; @AutoMap() chain_id: number; + @AutoMap() + reason: string; } export class ReportedAbuseItem { @@ -40,6 +55,7 @@ export class ReportedAbuseItem { escrowAddress: string; chainId: number; status: string; + reason: string; } export class ReportedAbuseResponse { diff --git a/packages/apps/human-app/server/src/modules/abuse/spec/abuse.fixtures.ts b/packages/apps/human-app/server/src/modules/abuse/spec/abuse.fixtures.ts index f0a0fe6439..84e9758301 100644 --- a/packages/apps/human-app/server/src/modules/abuse/spec/abuse.fixtures.ts +++ b/packages/apps/human-app/server/src/modules/abuse/spec/abuse.fixtures.ts @@ -10,16 +10,19 @@ const ESCROW_ADDRESS = 'test_address'; const CHAIN_ID = 1; const STATUS = 'reported'; const ABUSE_ID = 1; +const REASON = 'Test abuse reason'; export const TOKEN = 'test_user_token'; export const reportAbuseDtoFixture: ReportAbuseDto = { chain_id: CHAIN_ID, escrow_address: ESCROW_ADDRESS, + reason: REASON, }; export const reportAbuseParamsFixture: ReportAbuseParams = { chainId: CHAIN_ID, escrowAddress: ESCROW_ADDRESS, + reason: REASON, }; export const reportAbuseCommandFixture: ReportAbuseCommand = { @@ -32,6 +35,7 @@ export const reportedAbuseItemFixture: ReportedAbuseItem = { escrowAddress: ESCROW_ADDRESS, chainId: CHAIN_ID, status: STATUS, + reason: REASON, }; export const reportedAbuseResponseFixture: ReportedAbuseResponse = { diff --git a/packages/apps/reputation-oracle/server/src/database/migrations/1750766313641-AddReasonToAbuse.ts b/packages/apps/reputation-oracle/server/src/database/migrations/1750766313641-AddReasonToAbuse.ts new file mode 100644 index 0000000000..da01de6e2f --- /dev/null +++ b/packages/apps/reputation-oracle/server/src/database/migrations/1750766313641-AddReasonToAbuse.ts @@ -0,0 +1,13 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddReasonToAbuse1750766313641 implements MigrationInterface { + name = 'AddReasonToAbuse1750766313641'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "hmt"."abuses" ADD "reason" text NOT NULL`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "hmt"."abuses" DROP COLUMN "reason"`); + } +} diff --git a/packages/apps/reputation-oracle/server/src/modules/abuse/abuse.controller.ts b/packages/apps/reputation-oracle/server/src/modules/abuse/abuse.controller.ts index 35d4273953..ae1f146530 100644 --- a/packages/apps/reputation-oracle/server/src/modules/abuse/abuse.controller.ts +++ b/packages/apps/reputation-oracle/server/src/modules/abuse/abuse.controller.ts @@ -55,6 +55,7 @@ export class AbuseController { escrowAddress: data.escrowAddress, chainId: data.chainId, userId: request.user.id, + reason: data.reason, }); } @@ -82,6 +83,7 @@ export class AbuseController { escrowAddress: abuseEntity.escrowAddress, chainId: abuseEntity.chainId, status: abuseEntity.status, + reason: abuseEntity.reason, }; }); } diff --git a/packages/apps/reputation-oracle/server/src/modules/abuse/abuse.dto.ts b/packages/apps/reputation-oracle/server/src/modules/abuse/abuse.dto.ts index b828c78490..55afd67b15 100644 --- a/packages/apps/reputation-oracle/server/src/modules/abuse/abuse.dto.ts +++ b/packages/apps/reputation-oracle/server/src/modules/abuse/abuse.dto.ts @@ -1,6 +1,6 @@ import { ChainId } from '@human-protocol/sdk'; import { ApiProperty } from '@nestjs/swagger'; -import { IsEthereumAddress, IsString } from 'class-validator'; +import { IsEthereumAddress, IsString, MaxLength } from 'class-validator'; import { IsChainId } from '../../common/validators'; import { AbuseStatus } from './constants'; @@ -13,6 +13,15 @@ export class ReportAbuseDto { @ApiProperty({ name: 'escrow_address' }) @IsEthereumAddress() escrowAddress: string; + + @ApiProperty({ + name: 'reason', + required: true, + description: 'Reason for the abuse report', + }) + @IsString() + @MaxLength(1000) + reason: string; } export class AbuseResponseDto { @@ -27,6 +36,9 @@ export class AbuseResponseDto { @ApiProperty({ description: 'Current status of the abuse report' }) status: AbuseStatus; + + @ApiProperty({ description: 'Reason for the abuse report', required: true }) + reason: string; } export class SlackInteractionDto { diff --git a/packages/apps/reputation-oracle/server/src/modules/abuse/abuse.entity.ts b/packages/apps/reputation-oracle/server/src/modules/abuse/abuse.entity.ts index 1f5749b370..2f436a1afb 100644 --- a/packages/apps/reputation-oracle/server/src/modules/abuse/abuse.entity.ts +++ b/packages/apps/reputation-oracle/server/src/modules/abuse/abuse.entity.ts @@ -31,6 +31,9 @@ export class AbuseEntity extends BaseEntity { @Column({ type: 'decimal', precision: 30, scale: 18, nullable: true }) amount: number | null; + @Column({ type: 'text' }) + reason: string; + @JoinColumn() @ManyToOne('UserEntity', { nullable: false, onDelete: 'CASCADE' }) user?: UserEntity; diff --git a/packages/apps/reputation-oracle/server/src/modules/abuse/abuse.service.spec.ts b/packages/apps/reputation-oracle/server/src/modules/abuse/abuse.service.spec.ts index e39275ea72..90c31e8dcb 100644 --- a/packages/apps/reputation-oracle/server/src/modules/abuse/abuse.service.spec.ts +++ b/packages/apps/reputation-oracle/server/src/modules/abuse/abuse.service.spec.ts @@ -73,17 +73,20 @@ describe('AbuseService', () => { describe('reportAbuse', () => { it('should create a new abuse entity', async () => { const userId = faker.number.int(); + const reason = faker.lorem.sentence(); await abuseService.reportAbuse({ escrowAddress, chainId, userId, + reason, }); expect(mockAbuseRepository.createUnique).toHaveBeenCalledWith({ escrowAddress: escrowAddress, chainId: chainId, userId: userId, + reason: reason, retriesCount: 0, status: AbuseStatus.PENDING, waitUntil: expect.any(Date), diff --git a/packages/apps/reputation-oracle/server/src/modules/abuse/abuse.service.ts b/packages/apps/reputation-oracle/server/src/modules/abuse/abuse.service.ts index 80853720eb..1ac68c5f12 100644 --- a/packages/apps/reputation-oracle/server/src/modules/abuse/abuse.service.ts +++ b/packages/apps/reputation-oracle/server/src/modules/abuse/abuse.service.ts @@ -42,6 +42,7 @@ export class AbuseService { abuseEntity.escrowAddress = data.escrowAddress; abuseEntity.chainId = data.chainId; abuseEntity.userId = data.userId; + abuseEntity.reason = data.reason; abuseEntity.status = AbuseStatus.PENDING; abuseEntity.retriesCount = 0; abuseEntity.waitUntil = new Date(); diff --git a/packages/apps/reputation-oracle/server/src/modules/abuse/fixtures/index.ts b/packages/apps/reputation-oracle/server/src/modules/abuse/fixtures/index.ts index e79e622b2b..c75c6a8826 100644 --- a/packages/apps/reputation-oracle/server/src/modules/abuse/fixtures/index.ts +++ b/packages/apps/reputation-oracle/server/src/modules/abuse/fixtures/index.ts @@ -19,6 +19,7 @@ export function generateAbuseEntity( status: AbuseStatus.PENDING, decision: null, amount: null, + reason: faker.lorem.sentence(), waitUntil: faker.date.future(), createdAt: faker.date.recent(), updatedAt: new Date(), diff --git a/packages/apps/reputation-oracle/server/src/modules/abuse/types.ts b/packages/apps/reputation-oracle/server/src/modules/abuse/types.ts index 9e733cea43..37dec9605c 100644 --- a/packages/apps/reputation-oracle/server/src/modules/abuse/types.ts +++ b/packages/apps/reputation-oracle/server/src/modules/abuse/types.ts @@ -4,6 +4,7 @@ export type ReportAbuseInput = { userId: number; chainId: ChainId; escrowAddress: string; + reason: string; }; export type SlackInteractionBase = {