98 lines
2.8 KiB
TypeScript
98 lines
2.8 KiB
TypeScript
import { Injectable, Logger, HttpException, HttpStatus } from '@nestjs/common';
|
||
import { ConfigService } from '@nestjs/config';
|
||
import axios, { AxiosInstance } from 'axios';
|
||
|
||
/**
|
||
* Identity Service 客户端
|
||
* 用于调用 identity-service 的 API
|
||
*/
|
||
@Injectable()
|
||
export class IdentityClientService {
|
||
private readonly logger = new Logger(IdentityClientService.name);
|
||
private readonly httpClient: AxiosInstance;
|
||
|
||
constructor(private readonly configService: ConfigService) {
|
||
const baseUrl = this.configService.get<string>('IDENTITY_SERVICE_URL', 'http://localhost:3001');
|
||
|
||
this.httpClient = axios.create({
|
||
baseURL: baseUrl,
|
||
timeout: 10000,
|
||
});
|
||
|
||
this.logger.log(`Identity client initialized: ${baseUrl}`);
|
||
}
|
||
|
||
/**
|
||
* 验证用户的 TOTP 码
|
||
*
|
||
* @param userId 用户 ID
|
||
* @param totpCode TOTP 验证码
|
||
* @param token JWT token (用于认证)
|
||
* @returns 验证是否成功
|
||
*/
|
||
async verifyTotp(userId: string, totpCode: string, token: string): Promise<boolean> {
|
||
try {
|
||
this.logger.log(`验证 TOTP: userId=${userId}`);
|
||
|
||
const response = await this.httpClient.post(
|
||
'/totp/verify',
|
||
{ code: totpCode },
|
||
{
|
||
headers: {
|
||
Authorization: `Bearer ${token}`,
|
||
},
|
||
},
|
||
);
|
||
|
||
const valid = response.data?.valid ?? false;
|
||
this.logger.log(`TOTP 验证结果: userId=${userId}, valid=${valid}`);
|
||
|
||
return valid;
|
||
} catch (error: any) {
|
||
this.logger.error(`TOTP 验证失败: userId=${userId}, error=${error.message}`);
|
||
|
||
// 如果是 identity-service 返回的错误
|
||
if (error.response) {
|
||
const status = error.response.status;
|
||
const message = error.response.data?.message || 'TOTP 验证失败';
|
||
|
||
if (status === 400 || status === 401) {
|
||
throw new HttpException(message, HttpStatus.BAD_REQUEST);
|
||
}
|
||
}
|
||
|
||
// 网络错误或其他错误
|
||
throw new HttpException('TOTP 验证服务不可用', HttpStatus.SERVICE_UNAVAILABLE);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查用户是否启用了 TOTP
|
||
*
|
||
* @param userId 用户 ID
|
||
* @param token JWT token
|
||
* @returns 是否启用 TOTP
|
||
*/
|
||
async isTotpEnabled(userId: string, token: string): Promise<boolean> {
|
||
try {
|
||
this.logger.log(`检查 TOTP 状态: userId=${userId}`);
|
||
|
||
const response = await this.httpClient.get('/totp/status', {
|
||
headers: {
|
||
Authorization: `Bearer ${token}`,
|
||
},
|
||
});
|
||
|
||
const isEnabled = response.data?.isEnabled ?? false;
|
||
this.logger.log(`TOTP 状态: userId=${userId}, enabled=${isEnabled}`);
|
||
|
||
return isEnabled;
|
||
} catch (error: any) {
|
||
this.logger.error(`获取 TOTP 状态失败: userId=${userId}, error=${error.message}`);
|
||
|
||
// 如果获取状态失败,假设未启用 TOTP(允许操作继续)
|
||
return false;
|
||
}
|
||
}
|
||
}
|