112 lines
2.9 KiB
TypeScript
112 lines
2.9 KiB
TypeScript
import { Controller, Get, Post, Body, UseGuards } from '@nestjs/common';
|
|
import {
|
|
ApiTags,
|
|
ApiOperation,
|
|
ApiBearerAuth,
|
|
ApiResponse,
|
|
} from '@nestjs/swagger';
|
|
import { TotpService } from '@/application/services/totp.service';
|
|
import { CurrentUser, CurrentUserPayload } from '@/shared/decorators';
|
|
import { JwtAuthGuard } from '@/shared/guards/jwt-auth.guard';
|
|
|
|
class SetupTotpResponseDto {
|
|
secret: string;
|
|
qrCodeUrl: string;
|
|
manualEntryKey: string;
|
|
}
|
|
|
|
class TotpStatusResponseDto {
|
|
isEnabled: boolean;
|
|
isSetup: boolean;
|
|
enabledAt: Date | null;
|
|
}
|
|
|
|
class EnableTotpDto {
|
|
code: string;
|
|
}
|
|
|
|
class DisableTotpDto {
|
|
code: string;
|
|
}
|
|
|
|
class VerifyTotpDto {
|
|
code: string;
|
|
}
|
|
|
|
@ApiTags('TOTP')
|
|
@Controller('totp')
|
|
@UseGuards(JwtAuthGuard)
|
|
@ApiBearerAuth()
|
|
export class TotpController {
|
|
constructor(private readonly totpService: TotpService) {}
|
|
|
|
@Get('status')
|
|
@ApiOperation({
|
|
summary: '获取 TOTP 状态',
|
|
description: '查询当前用户的 TOTP 启用状态',
|
|
})
|
|
@ApiResponse({ status: 200, type: TotpStatusResponseDto })
|
|
async getStatus(
|
|
@CurrentUser() user: CurrentUserPayload,
|
|
): Promise<TotpStatusResponseDto> {
|
|
return this.totpService.getTotpStatus(BigInt(user.userId));
|
|
}
|
|
|
|
@Post('setup')
|
|
@ApiOperation({
|
|
summary: '设置 TOTP',
|
|
description: '生成 TOTP 密钥,返回二维码和手动输入密钥',
|
|
})
|
|
@ApiResponse({ status: 201, type: SetupTotpResponseDto })
|
|
async setup(
|
|
@CurrentUser() user: CurrentUserPayload,
|
|
): Promise<SetupTotpResponseDto> {
|
|
return this.totpService.setupTotp(BigInt(user.userId));
|
|
}
|
|
|
|
@Post('enable')
|
|
@ApiOperation({
|
|
summary: '启用 TOTP',
|
|
description: '验证码正确后启用 TOTP 二次验证',
|
|
})
|
|
@ApiResponse({ status: 200, description: 'TOTP 已启用' })
|
|
async enable(
|
|
@CurrentUser() user: CurrentUserPayload,
|
|
@Body() dto: EnableTotpDto,
|
|
): Promise<{ success: boolean; message: string }> {
|
|
await this.totpService.enableTotp(BigInt(user.userId), dto.code);
|
|
return { success: true, message: 'TOTP 已启用' };
|
|
}
|
|
|
|
@Post('disable')
|
|
@ApiOperation({
|
|
summary: '禁用 TOTP',
|
|
description: '验证码正确后禁用 TOTP 二次验证',
|
|
})
|
|
@ApiResponse({ status: 200, description: 'TOTP 已禁用' })
|
|
async disable(
|
|
@CurrentUser() user: CurrentUserPayload,
|
|
@Body() dto: DisableTotpDto,
|
|
): Promise<{ success: boolean; message: string }> {
|
|
await this.totpService.disableTotp(BigInt(user.userId), dto.code);
|
|
return { success: true, message: 'TOTP 已禁用' };
|
|
}
|
|
|
|
@Post('verify')
|
|
@ApiOperation({
|
|
summary: '验证 TOTP',
|
|
description: '验证 TOTP 验证码是否正确',
|
|
})
|
|
@ApiResponse({ status: 200, description: '验证结果' })
|
|
async verify(
|
|
@CurrentUser() user: CurrentUserPayload,
|
|
@Body() dto: VerifyTotpDto,
|
|
): Promise<{ valid: boolean }> {
|
|
const valid = await this.totpService.verifyTotp(
|
|
BigInt(user.userId),
|
|
dto.code,
|
|
);
|
|
return { valid };
|
|
}
|
|
}
|