feat: implement ReportsModule with service, controller, and endpoints
Added ReportsModule to manage user reports. Includes service methods for creating, retrieving, and updating report statuses, as well as controller endpoints for handling these operations. Integrated with authentication, role-based access control, and database logic.
This commit is contained in:
25
backend/src/reports/dto/create-report.dto.ts
Normal file
25
backend/src/reports/dto/create-report.dto.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { IsEnum, IsOptional, IsString, IsUUID } from "class-validator";
|
||||||
|
|
||||||
|
export enum ReportReason {
|
||||||
|
INAPPROPRIATE = "inappropriate",
|
||||||
|
SPAM = "spam",
|
||||||
|
COPYRIGHT = "copyright",
|
||||||
|
OTHER = "other",
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CreateReportDto {
|
||||||
|
@IsOptional()
|
||||||
|
@IsUUID()
|
||||||
|
contentId?: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsUUID()
|
||||||
|
tagId?: string;
|
||||||
|
|
||||||
|
@IsEnum(ReportReason)
|
||||||
|
reason!: "inappropriate" | "spam" | "copyright" | "other";
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
13
backend/src/reports/dto/update-report-status.dto.ts
Normal file
13
backend/src/reports/dto/update-report-status.dto.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { IsEnum } from "class-validator";
|
||||||
|
|
||||||
|
export enum ReportStatus {
|
||||||
|
PENDING = "pending",
|
||||||
|
REVIEWED = "reviewed",
|
||||||
|
RESOLVED = "resolved",
|
||||||
|
DISMISSED = "dismissed",
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UpdateReportStatusDto {
|
||||||
|
@IsEnum(ReportStatus)
|
||||||
|
status!: "pending" | "reviewed" | "resolved" | "dismissed";
|
||||||
|
}
|
||||||
54
backend/src/reports/reports.controller.ts
Normal file
54
backend/src/reports/reports.controller.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
DefaultValuePipe,
|
||||||
|
Get,
|
||||||
|
Param,
|
||||||
|
ParseIntPipe,
|
||||||
|
Patch,
|
||||||
|
Post,
|
||||||
|
Query,
|
||||||
|
Req,
|
||||||
|
UseGuards,
|
||||||
|
} from "@nestjs/common";
|
||||||
|
import { Roles } from "../auth/decorators/roles.decorator";
|
||||||
|
import { AuthGuard } from "../auth/guards/auth.guard";
|
||||||
|
import { RolesGuard } from "../auth/guards/roles.guard";
|
||||||
|
import type { AuthenticatedRequest } from "../common/interfaces/request.interface";
|
||||||
|
import { CreateReportDto } from "./dto/create-report.dto";
|
||||||
|
import { UpdateReportStatusDto } from "./dto/update-report-status.dto";
|
||||||
|
import { ReportsService } from "./reports.service";
|
||||||
|
|
||||||
|
@Controller("reports")
|
||||||
|
export class ReportsController {
|
||||||
|
constructor(private readonly reportsService: ReportsService) {}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
create(
|
||||||
|
@Req() req: AuthenticatedRequest,
|
||||||
|
@Body() createReportDto: CreateReportDto,
|
||||||
|
) {
|
||||||
|
return this.reportsService.create(req.user.sub, createReportDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
@UseGuards(AuthGuard, RolesGuard)
|
||||||
|
@Roles("admin", "moderator")
|
||||||
|
findAll(
|
||||||
|
@Query("limit", new DefaultValuePipe(10), ParseIntPipe) limit: number,
|
||||||
|
@Query("offset", new DefaultValuePipe(0), ParseIntPipe) offset: number,
|
||||||
|
) {
|
||||||
|
return this.reportsService.findAll(limit, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch(":id/status")
|
||||||
|
@UseGuards(AuthGuard, RolesGuard)
|
||||||
|
@Roles("admin", "moderator")
|
||||||
|
updateStatus(
|
||||||
|
@Param("id") id: string,
|
||||||
|
@Body() updateReportStatusDto: UpdateReportStatusDto,
|
||||||
|
) {
|
||||||
|
return this.reportsService.updateStatus(id, updateReportStatusDto.status);
|
||||||
|
}
|
||||||
|
}
|
||||||
13
backend/src/reports/reports.module.ts
Normal file
13
backend/src/reports/reports.module.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { Module } from "@nestjs/common";
|
||||||
|
import { AuthModule } from "../auth/auth.module";
|
||||||
|
import { CryptoModule } from "../crypto/crypto.module";
|
||||||
|
import { DatabaseModule } from "../database/database.module";
|
||||||
|
import { ReportsController } from "./reports.controller";
|
||||||
|
import { ReportsService } from "./reports.service";
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [DatabaseModule, AuthModule, CryptoModule],
|
||||||
|
controllers: [ReportsController],
|
||||||
|
providers: [ReportsService],
|
||||||
|
})
|
||||||
|
export class ReportsModule {}
|
||||||
44
backend/src/reports/reports.service.ts
Normal file
44
backend/src/reports/reports.service.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { Injectable } from "@nestjs/common";
|
||||||
|
import { desc, eq } from "drizzle-orm";
|
||||||
|
import { DatabaseService } from "../database/database.service";
|
||||||
|
import { reports } from "../database/schemas";
|
||||||
|
import { CreateReportDto } from "./dto/create-report.dto";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ReportsService {
|
||||||
|
constructor(private readonly databaseService: DatabaseService) {}
|
||||||
|
|
||||||
|
async create(reporterId: string, data: CreateReportDto) {
|
||||||
|
const [newReport] = await this.databaseService.db
|
||||||
|
.insert(reports)
|
||||||
|
.values({
|
||||||
|
reporterId,
|
||||||
|
contentId: data.contentId,
|
||||||
|
tagId: data.tagId,
|
||||||
|
reason: data.reason,
|
||||||
|
description: data.description,
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
return newReport;
|
||||||
|
}
|
||||||
|
|
||||||
|
async findAll(limit: number, offset: number) {
|
||||||
|
return await this.databaseService.db
|
||||||
|
.select()
|
||||||
|
.from(reports)
|
||||||
|
.orderBy(desc(reports.createdAt))
|
||||||
|
.limit(limit)
|
||||||
|
.offset(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateStatus(
|
||||||
|
id: string,
|
||||||
|
status: "pending" | "reviewed" | "resolved" | "dismissed",
|
||||||
|
) {
|
||||||
|
return await this.databaseService.db
|
||||||
|
.update(reports)
|
||||||
|
.set({ status, updatedAt: new Date() })
|
||||||
|
.where(eq(reports.id, id))
|
||||||
|
.returning();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user