- Remove `type` modifier from imports used as DI constructor params across ~235 files (@Injectable, @Controller, @Module, @Catch, @CommandHandler, @QueryHandler, @EventsHandler, @WebSocketGateway). TypeScript emitDecoratorMetadata strips type-only imports, leaving Reflect.metadata with Function placeholder and breaking Nest DI. - Fix controllers: DTOs used with @Body/@Query/@Param must be runtime imports so ValidationPipe can whitelist properties. Previously returned 400 "property X should not exist" on every request. - Register ProjectsModule in AppModule (was defined but never wired). - Add approve()/reject() methods to TransferListingEntity referenced by ModerateTransferListingHandler. - Export BankTransferConfirmedEvent from payments barrel for subscription activation handler. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
88 lines
3.5 KiB
TypeScript
88 lines
3.5 KiB
TypeScript
import {
|
|
Body,
|
|
Controller,
|
|
Delete,
|
|
Get,
|
|
Param,
|
|
Post,
|
|
UseGuards,
|
|
} from '@nestjs/common';
|
|
import { CommandBus } from '@nestjs/cqrs';
|
|
import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth } from '@nestjs/swagger';
|
|
import { CancelUserDeletionCommand } from '../../application/commands/cancel-user-deletion/cancel-user-deletion.command';
|
|
import { ExportUserDataCommand } from '../../application/commands/export-user-data/export-user-data.command';
|
|
import { type UserDataExport } from '../../application/commands/export-user-data/export-user-data.handler';
|
|
import { ForceDeleteUserCommand } from '../../application/commands/force-delete-user/force-delete-user.command';
|
|
import { RequestUserDeletionCommand } from '../../application/commands/request-user-deletion/request-user-deletion.command';
|
|
import { type JwtPayload } from '../../infrastructure/services/token.service';
|
|
import { CurrentUser } from '../decorators/current-user.decorator';
|
|
import { Roles } from '../decorators/roles.decorator';
|
|
import { ForceDeleteUserDto } from '../dto/force-delete-user.dto';
|
|
import { RequestDeletionDto } from '../dto/request-deletion.dto';
|
|
import { JwtAuthGuard } from '../guards/jwt-auth.guard';
|
|
import { RolesGuard } from '../guards/roles.guard';
|
|
|
|
@ApiTags('users')
|
|
@Controller('users')
|
|
export class UserDataController {
|
|
constructor(private readonly commandBus: CommandBus) {}
|
|
|
|
@Delete('me')
|
|
@UseGuards(JwtAuthGuard)
|
|
@ApiBearerAuth('JWT')
|
|
@ApiOperation({ summary: 'Request account deletion (30-day grace period)' })
|
|
@ApiResponse({ status: 200, description: 'Deletion scheduled' })
|
|
@ApiResponse({ status: 401, description: 'Unauthorized' })
|
|
async requestDeletion(
|
|
@CurrentUser() user: JwtPayload,
|
|
@Body() dto: RequestDeletionDto,
|
|
): Promise<{ scheduledAt: Date; message: string }> {
|
|
const result = await this.commandBus.execute(
|
|
new RequestUserDeletionCommand(user.sub, dto.reason),
|
|
);
|
|
return { ...result, message: 'Tài khoản sẽ bị xóa sau 30 ngày' };
|
|
}
|
|
|
|
@Post('me/cancel-deletion')
|
|
@UseGuards(JwtAuthGuard)
|
|
@ApiBearerAuth('JWT')
|
|
@ApiOperation({ summary: 'Cancel pending account deletion' })
|
|
@ApiResponse({ status: 201, description: 'Deletion cancelled' })
|
|
@ApiResponse({ status: 401, description: 'Unauthorized' })
|
|
async cancelDeletion(
|
|
@CurrentUser() user: JwtPayload,
|
|
): Promise<{ message: string }> {
|
|
return this.commandBus.execute(new CancelUserDeletionCommand(user.sub));
|
|
}
|
|
|
|
@Get('me/export')
|
|
@UseGuards(JwtAuthGuard)
|
|
@ApiBearerAuth('JWT')
|
|
@ApiOperation({ summary: 'Export user data (GDPR Article 20)' })
|
|
@ApiResponse({ status: 200, description: 'User data exported as JSON' })
|
|
@ApiResponse({ status: 401, description: 'Unauthorized' })
|
|
async exportData(
|
|
@CurrentUser() user: JwtPayload,
|
|
): Promise<UserDataExport> {
|
|
return this.commandBus.execute(new ExportUserDataCommand(user.sub));
|
|
}
|
|
|
|
@Delete(':id/force')
|
|
@UseGuards(JwtAuthGuard, RolesGuard)
|
|
@Roles('ADMIN')
|
|
@ApiBearerAuth('JWT')
|
|
@ApiOperation({ summary: 'Force-delete user immediately (admin only)' })
|
|
@ApiResponse({ status: 200, description: 'User force-deleted' })
|
|
@ApiResponse({ status: 401, description: 'Unauthorized' })
|
|
@ApiResponse({ status: 403, description: 'Forbidden — admin only' })
|
|
async forceDelete(
|
|
@Param('id') id: string,
|
|
@CurrentUser() admin: JwtPayload,
|
|
@Body() dto: ForceDeleteUserDto,
|
|
): Promise<{ message: string }> {
|
|
return this.commandBus.execute(
|
|
new ForceDeleteUserCommand(id, admin.sub, dto.reason),
|
|
);
|
|
}
|
|
}
|