refactor: move backup-service client from identity-service to mpc-service
Architecture change: delegate share storage is now handled by mpc-service. - identity-service no longer calls backup-service directly - mpc-service calls backup-service after keygen completion - This follows proper domain boundaries (MPC domain handles share storage) Flow: 1. identity-service publishes mpc.KeygenRequested 2. mpc-service calls mpc-system for keygen 3. mpc-service stores delegate share to backup-service 4. mpc-service publishes mpc.KeygenCompleted 5. identity-service updates user wallet address 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
f4f0466616
commit
383a9540a0
|
|
@ -32,7 +32,6 @@ import { EventPublisherService } from '@/infrastructure/kafka/event-publisher.se
|
|||
import { MpcEventConsumerService } from '@/infrastructure/kafka/mpc-event-consumer.service';
|
||||
import { SmsService } from '@/infrastructure/external/sms/sms.service';
|
||||
import { MpcClientService, MpcWalletService } from '@/infrastructure/external/mpc';
|
||||
import { BackupClientService, MpcShareStorageService } from '@/infrastructure/external/backup';
|
||||
import { WalletGeneratorServiceImpl } from '@/infrastructure/external/blockchain/wallet-generator.service.impl';
|
||||
|
||||
// Shared
|
||||
|
|
@ -56,8 +55,6 @@ import { JwtAuthGuard } from '@/shared/guards/jwt-auth.guard';
|
|||
SmsService,
|
||||
MpcClientService,
|
||||
MpcWalletService,
|
||||
BackupClientService,
|
||||
MpcShareStorageService,
|
||||
// WalletGeneratorService 抽象类由 WalletGeneratorServiceImpl 实现
|
||||
WalletGeneratorServiceImpl,
|
||||
{ provide: WalletGeneratorService, useExisting: WalletGeneratorServiceImpl },
|
||||
|
|
@ -71,8 +68,6 @@ import { JwtAuthGuard } from '@/shared/guards/jwt-auth.guard';
|
|||
SmsService,
|
||||
MpcClientService,
|
||||
MpcWalletService,
|
||||
BackupClientService,
|
||||
MpcShareStorageService,
|
||||
WalletGeneratorService,
|
||||
MPC_KEY_SHARE_REPOSITORY,
|
||||
],
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import { RedisService } from '@/infrastructure/redis/redis.service';
|
|||
import { SmsService } from '@/infrastructure/external/sms/sms.service';
|
||||
import { EventPublisherService } from '@/infrastructure/kafka/event-publisher.service';
|
||||
import { MpcWalletService } from '@/infrastructure/external/mpc';
|
||||
import { BackupClientService } from '@/infrastructure/external/backup';
|
||||
|
||||
describe('UserApplicationService - Referral APIs', () => {
|
||||
let service: UserApplicationService;
|
||||
|
|
@ -131,10 +130,6 @@ describe('UserApplicationService - Referral APIs', () => {
|
|||
generateMpcWallet: jest.fn(),
|
||||
};
|
||||
|
||||
const mockBackupClientService = {
|
||||
storeBackupShare: jest.fn(),
|
||||
};
|
||||
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
UserApplicationService,
|
||||
|
|
@ -182,10 +177,6 @@ describe('UserApplicationService - Referral APIs', () => {
|
|||
provide: MpcWalletService,
|
||||
useValue: mockMpcWalletService,
|
||||
},
|
||||
{
|
||||
provide: BackupClientService,
|
||||
useValue: mockBackupClientService,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import { RedisService } from '@/infrastructure/redis/redis.service';
|
|||
import { SmsService } from '@/infrastructure/external/sms/sms.service';
|
||||
import { EventPublisherService } from '@/infrastructure/kafka/event-publisher.service';
|
||||
import { MpcWalletService } from '@/infrastructure/external/mpc';
|
||||
import { BackupClientService } from '@/infrastructure/external/backup';
|
||||
import { ApplicationError } from '@/shared/exceptions/domain.exception';
|
||||
import { generateRandomIdentity } from '@/shared/utils';
|
||||
import {
|
||||
|
|
@ -43,7 +42,6 @@ export class UserApplicationService {
|
|||
private readonly validatorService: UserValidatorService,
|
||||
private readonly walletGenerator: WalletGeneratorService,
|
||||
private readonly mpcWalletService: MpcWalletService,
|
||||
private readonly backupClient: BackupClientService,
|
||||
private readonly tokenService: TokenService,
|
||||
private readonly redisService: RedisService,
|
||||
private readonly smsService: SmsService,
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
export * from './backup-client.service';
|
||||
export * from './mpc-share-storage.service';
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
/**
|
||||
* MPC Share Storage Service
|
||||
*
|
||||
* 封装 MPC 分片存储逻辑,提供简化接口给应用层
|
||||
*/
|
||||
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { BackupClientService } from './backup-client.service';
|
||||
|
||||
export interface MpcStoreBackupShareParams {
|
||||
userId: string;
|
||||
shareData: string;
|
||||
publicKey: string;
|
||||
accountSequence?: number;
|
||||
}
|
||||
|
||||
export interface MpcRetrieveBackupShareParams {
|
||||
userId: string;
|
||||
publicKey: string;
|
||||
recoveryToken: string;
|
||||
deviceId?: string;
|
||||
}
|
||||
|
||||
export interface MpcBackupShareData {
|
||||
encryptedShareData: string;
|
||||
partyIndex: number;
|
||||
publicKey: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class MpcShareStorageService {
|
||||
private readonly logger = new Logger(MpcShareStorageService.name);
|
||||
|
||||
constructor(private readonly backupClient: BackupClientService) {}
|
||||
|
||||
/**
|
||||
* 存储备份分片
|
||||
*
|
||||
* @param params 分片存储参数
|
||||
*/
|
||||
async storeBackupShare(params: MpcStoreBackupShareParams): Promise<void> {
|
||||
this.logger.log(`Storing backup share for user=${params.userId}`);
|
||||
|
||||
await this.backupClient.storeBackupShare({
|
||||
userId: params.userId,
|
||||
accountSequence: params.accountSequence || 0,
|
||||
publicKey: params.publicKey,
|
||||
encryptedShareData: params.shareData,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取备份分片 (用于账户恢复)
|
||||
*
|
||||
* @param params 分片获取参数
|
||||
* @returns 备份分片数据或 null
|
||||
*/
|
||||
async retrieveBackupShare(
|
||||
params: MpcRetrieveBackupShareParams,
|
||||
): Promise<MpcBackupShareData | null> {
|
||||
this.logger.log(`Retrieving backup share for user=${params.userId}`);
|
||||
|
||||
return this.backupClient.retrieveBackupShare(params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤销备份分片
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @param publicKey MPC 公钥
|
||||
* @param reason 撤销原因
|
||||
*/
|
||||
async revokeBackupShare(
|
||||
userId: string,
|
||||
publicKey: string,
|
||||
reason: string,
|
||||
): Promise<void> {
|
||||
this.logger.log(`Revoking backup share for user=${userId}`);
|
||||
|
||||
await this.backupClient.revokeBackupShare(userId, publicKey, reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查备份服务是否可用
|
||||
*/
|
||||
isEnabled(): boolean {
|
||||
return this.backupClient.isEnabled();
|
||||
}
|
||||
}
|
||||
|
|
@ -11,7 +11,6 @@ import { SmsService } from './external/sms/sms.service';
|
|||
import { WalletGeneratorServiceImpl } from './external/blockchain/wallet-generator.service.impl';
|
||||
import { BlockchainQueryService } from './external/blockchain/blockchain-query.service';
|
||||
import { MpcClientService, MpcWalletService } from './external/mpc';
|
||||
import { BackupClientService, MpcShareStorageService } from './external/backup';
|
||||
import { MPC_KEY_SHARE_REPOSITORY } from '@/domain/repositories/mpc-key-share.repository.interface';
|
||||
import { WalletGeneratorService } from '@/domain/services/wallet-generator.service';
|
||||
|
||||
|
|
@ -44,8 +43,6 @@ import { WalletGeneratorService } from '@/domain/services/wallet-generator.servi
|
|||
BlockchainQueryService,
|
||||
MpcClientService,
|
||||
MpcWalletService,
|
||||
BackupClientService,
|
||||
MpcShareStorageService,
|
||||
],
|
||||
exports: [
|
||||
PrismaService,
|
||||
|
|
@ -64,8 +61,6 @@ import { WalletGeneratorService } from '@/domain/services/wallet-generator.servi
|
|||
BlockchainQueryService,
|
||||
MpcClientService,
|
||||
MpcWalletService,
|
||||
BackupClientService,
|
||||
MpcShareStorageService,
|
||||
],
|
||||
})
|
||||
export class InfrastructureModule {}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import {
|
|||
MPC_CONSUME_TOPICS,
|
||||
KeygenRequestedPayload,
|
||||
} from '../../infrastructure/messaging/kafka/event-consumer.service';
|
||||
import { BackupClientService } from '../../infrastructure/external/backup';
|
||||
import { KeygenStartedEvent } from '../../domain/events/keygen-started.event';
|
||||
import { KeygenCompletedEvent } from '../../domain/events/keygen-completed.event';
|
||||
import { SessionFailedEvent } from '../../domain/events/session-failed.event';
|
||||
|
|
@ -26,6 +27,7 @@ export class KeygenRequestedHandler implements OnModuleInit {
|
|||
private readonly eventConsumer: EventConsumerService,
|
||||
private readonly eventPublisher: EventPublisherService,
|
||||
private readonly mpcCoordinator: MPCCoordinatorService,
|
||||
private readonly backupClient: BackupClientService,
|
||||
) {}
|
||||
|
||||
async onModuleInit() {
|
||||
|
|
@ -72,14 +74,31 @@ export class KeygenRequestedHandler implements OnModuleInit {
|
|||
// Cache public key
|
||||
await this.mpcCoordinator.savePublicKeyCache(username, result.publicKey);
|
||||
|
||||
// Save delegate share if exists
|
||||
// Save delegate share to backup-service if exists
|
||||
if (result.delegateShare) {
|
||||
// 1. 保存到本地缓存(用于签名时快速访问)
|
||||
await this.mpcCoordinator.saveDelegateShare({
|
||||
username,
|
||||
partyId: result.delegateShare.partyId,
|
||||
partyIndex: result.delegateShare.partyIndex,
|
||||
encryptedShare: result.delegateShare.encryptedShare,
|
||||
});
|
||||
|
||||
// 2. 存储到 backup-service(用于灾备恢复)
|
||||
try {
|
||||
await this.backupClient.storeBackupShare({
|
||||
userId,
|
||||
username,
|
||||
publicKey: result.publicKey,
|
||||
partyId: result.delegateShare.partyId,
|
||||
partyIndex: result.delegateShare.partyIndex,
|
||||
encryptedShare: result.delegateShare.encryptedShare,
|
||||
});
|
||||
this.logger.log(`Delegate share stored to backup-service: userId=${userId}`);
|
||||
} catch (backupError) {
|
||||
// 备份失败不阻塞主流程,但记录错误
|
||||
this.logger.error(`Failed to store delegate share to backup-service: userId=${userId}`, backupError);
|
||||
}
|
||||
}
|
||||
|
||||
// Publish success event
|
||||
|
|
|
|||
|
|
@ -1,192 +1,155 @@
|
|||
/**
|
||||
* Backup Client Service
|
||||
*
|
||||
* 与 backup-service 通信的客户端服务
|
||||
* 负责存储和获取 MPC Backup Share (Party 2)
|
||||
*
|
||||
* 安全要求: backup-service 必须部署在与 identity-service 不同的物理服务器上
|
||||
*/
|
||||
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { HttpService } from '@nestjs/axios';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import * as jwt from 'jsonwebtoken';
|
||||
|
||||
export interface StoreBackupShareParams {
|
||||
userId: string;
|
||||
accountSequence: number;
|
||||
publicKey: string;
|
||||
encryptedShareData: string;
|
||||
}
|
||||
|
||||
export interface RetrieveBackupShareParams {
|
||||
userId: string;
|
||||
publicKey: string;
|
||||
recoveryToken: string;
|
||||
deviceId?: string;
|
||||
}
|
||||
|
||||
export interface BackupShareResult {
|
||||
encryptedShareData: string;
|
||||
partyIndex: number;
|
||||
publicKey: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class BackupClientService {
|
||||
private readonly logger = new Logger(BackupClientService.name);
|
||||
private readonly backupServiceUrl: string;
|
||||
private readonly serviceJwtSecret: string;
|
||||
private readonly enabled: boolean;
|
||||
|
||||
constructor(
|
||||
private readonly httpService: HttpService,
|
||||
private readonly configService: ConfigService,
|
||||
) {
|
||||
this.backupServiceUrl = this.configService.get<string>('BACKUP_SERVICE_URL', 'http://localhost:3002');
|
||||
this.serviceJwtSecret = this.configService.get<string>('SERVICE_JWT_SECRET', '');
|
||||
this.enabled = this.configService.get<string>('BACKUP_SERVICE_ENABLED', 'false') === 'true';
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查 backup-service 是否启用
|
||||
*/
|
||||
isEnabled(): boolean {
|
||||
return this.enabled && !!this.serviceJwtSecret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 存储备份分片到 backup-service
|
||||
*/
|
||||
async storeBackupShare(params: StoreBackupShareParams): Promise<void> {
|
||||
if (!this.isEnabled()) {
|
||||
this.logger.warn('Backup service is disabled, skipping backup share storage');
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.log(`Storing backup share for user: ${params.userId}`);
|
||||
|
||||
try {
|
||||
const serviceToken = this.generateServiceToken();
|
||||
|
||||
await firstValueFrom(
|
||||
this.httpService.post(
|
||||
`${this.backupServiceUrl}/backup-share/store`,
|
||||
{
|
||||
userId: params.userId,
|
||||
accountSequence: params.accountSequence,
|
||||
publicKey: params.publicKey,
|
||||
encryptedShareData: params.encryptedShareData,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Service-Token': serviceToken,
|
||||
},
|
||||
timeout: 30000, // 30秒超时
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
this.logger.log(`Backup share stored successfully for user: ${params.userId}`);
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to store backup share for user: ${params.userId}`, error);
|
||||
// 不抛出异常,允许账户创建继续
|
||||
// 可以通过补偿任务稍后重试
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 backup-service 获取备份分片 (用于账户恢复)
|
||||
*/
|
||||
async retrieveBackupShare(params: RetrieveBackupShareParams): Promise<BackupShareResult | null> {
|
||||
if (!this.isEnabled()) {
|
||||
this.logger.warn('Backup service is disabled');
|
||||
return null;
|
||||
}
|
||||
|
||||
this.logger.log(`Retrieving backup share for user: ${params.userId}`);
|
||||
|
||||
try {
|
||||
const serviceToken = this.generateServiceToken();
|
||||
|
||||
const response = await firstValueFrom(
|
||||
this.httpService.post<BackupShareResult>(
|
||||
`${this.backupServiceUrl}/backup-share/retrieve`,
|
||||
{
|
||||
userId: params.userId,
|
||||
publicKey: params.publicKey,
|
||||
recoveryToken: params.recoveryToken,
|
||||
deviceId: params.deviceId,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Service-Token': serviceToken,
|
||||
},
|
||||
timeout: 30000,
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
this.logger.log(`Backup share retrieved successfully for user: ${params.userId}`);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to retrieve backup share for user: ${params.userId}`, error);
|
||||
throw new Error(`Failed to retrieve backup share: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤销备份分片 (用于密钥轮换或账户注销)
|
||||
*/
|
||||
async revokeBackupShare(userId: string, publicKey: string, reason: string): Promise<void> {
|
||||
if (!this.isEnabled()) {
|
||||
this.logger.warn('Backup service is disabled');
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.log(`Revoking backup share for user: ${userId}, reason: ${reason}`);
|
||||
|
||||
try {
|
||||
const serviceToken = this.generateServiceToken();
|
||||
|
||||
await firstValueFrom(
|
||||
this.httpService.post(
|
||||
`${this.backupServiceUrl}/backup-share/revoke`,
|
||||
{
|
||||
userId,
|
||||
publicKey,
|
||||
reason,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Service-Token': serviceToken,
|
||||
},
|
||||
timeout: 30000,
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
this.logger.log(`Backup share revoked successfully for user: ${userId}`);
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to revoke backup share for user: ${userId}`, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成服务间认证 JWT
|
||||
*/
|
||||
private generateServiceToken(): string {
|
||||
return jwt.sign(
|
||||
{
|
||||
service: 'identity-service',
|
||||
iat: Math.floor(Date.now() / 1000),
|
||||
},
|
||||
this.serviceJwtSecret,
|
||||
{ expiresIn: '5m' },
|
||||
);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Backup Client Service
|
||||
*
|
||||
* mpc-service 调用 backup-service 存储/获取 delegate share
|
||||
* backup-service 负责安全存储 MPC 分片备份
|
||||
*/
|
||||
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { HttpService } from '@nestjs/axios';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import * as jwt from 'jsonwebtoken';
|
||||
|
||||
export interface StoreBackupShareParams {
|
||||
userId: string;
|
||||
username: string;
|
||||
publicKey: string;
|
||||
partyId: string;
|
||||
partyIndex: number;
|
||||
encryptedShare: string;
|
||||
}
|
||||
|
||||
export interface RetrieveBackupShareParams {
|
||||
userId: string;
|
||||
publicKey: string;
|
||||
}
|
||||
|
||||
export interface BackupShareResult {
|
||||
encryptedShareData: string;
|
||||
partyIndex: number;
|
||||
publicKey: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class BackupClientService {
|
||||
private readonly logger = new Logger(BackupClientService.name);
|
||||
private readonly backupServiceUrl: string;
|
||||
private readonly serviceJwtSecret: string;
|
||||
private readonly enabled: boolean;
|
||||
|
||||
constructor(
|
||||
private readonly httpService: HttpService,
|
||||
private readonly configService: ConfigService,
|
||||
) {
|
||||
this.backupServiceUrl = this.configService.get<string>('BACKUP_SERVICE_URL', 'http://localhost:3002');
|
||||
this.serviceJwtSecret = this.configService.get<string>('SERVICE_JWT_SECRET', '');
|
||||
this.enabled = this.configService.get<string>('BACKUP_SERVICE_ENABLED', 'true') === 'true';
|
||||
|
||||
this.logger.log(`[INIT] BackupClientService initialized`);
|
||||
this.logger.log(`[INIT] URL: ${this.backupServiceUrl}`);
|
||||
this.logger.log(`[INIT] Enabled: ${this.enabled}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查 backup-service 是否启用
|
||||
*/
|
||||
isEnabled(): boolean {
|
||||
return this.enabled && !!this.serviceJwtSecret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 存储 delegate share 到 backup-service
|
||||
*/
|
||||
async storeBackupShare(params: StoreBackupShareParams): Promise<void> {
|
||||
if (!this.isEnabled()) {
|
||||
this.logger.warn('Backup service is disabled, skipping backup share storage');
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.log(`Storing backup share: userId=${params.userId}, username=${params.username}`);
|
||||
|
||||
try {
|
||||
const serviceToken = this.generateServiceToken();
|
||||
|
||||
await firstValueFrom(
|
||||
this.httpService.post(
|
||||
`${this.backupServiceUrl}/backup-share/store`,
|
||||
{
|
||||
userId: params.userId,
|
||||
username: params.username,
|
||||
publicKey: params.publicKey,
|
||||
partyId: params.partyId,
|
||||
partyIndex: params.partyIndex,
|
||||
encryptedShareData: params.encryptedShare,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Service-Token': serviceToken,
|
||||
},
|
||||
timeout: 30000,
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
this.logger.log(`Backup share stored successfully: userId=${params.userId}`);
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to store backup share: userId=${params.userId}`, error);
|
||||
throw error; // 抛出异常,让调用方处理
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 backup-service 获取 delegate share (用于签名)
|
||||
*/
|
||||
async retrieveBackupShare(params: RetrieveBackupShareParams): Promise<BackupShareResult | null> {
|
||||
if (!this.isEnabled()) {
|
||||
this.logger.warn('Backup service is disabled');
|
||||
return null;
|
||||
}
|
||||
|
||||
this.logger.log(`Retrieving backup share: userId=${params.userId}`);
|
||||
|
||||
try {
|
||||
const serviceToken = this.generateServiceToken();
|
||||
|
||||
const response = await firstValueFrom(
|
||||
this.httpService.post<BackupShareResult>(
|
||||
`${this.backupServiceUrl}/backup-share/retrieve`,
|
||||
{
|
||||
userId: params.userId,
|
||||
publicKey: params.publicKey,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Service-Token': serviceToken,
|
||||
},
|
||||
timeout: 30000,
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
this.logger.log(`Backup share retrieved: userId=${params.userId}`);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to retrieve backup share: userId=${params.userId}`, error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成服务间认证 JWT
|
||||
*/
|
||||
private generateServiceToken(): string {
|
||||
return jwt.sign(
|
||||
{
|
||||
service: 'mpc-service',
|
||||
iat: Math.floor(Date.now() / 1000),
|
||||
},
|
||||
this.serviceJwtSecret,
|
||||
{ expiresIn: '5m' },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { BackupClientService } from './backup-client.service';
|
||||
|
|
@ -4,10 +4,12 @@
|
|||
* mpc-service 作为网关,需要:
|
||||
* - PrismaService 用于缓存公钥和 delegate share
|
||||
* - Kafka 事件发布和消费
|
||||
* - BackupClientService 用于存储 delegate share 到 backup-service
|
||||
*/
|
||||
|
||||
import { Global, Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { HttpModule } from '@nestjs/axios';
|
||||
|
||||
// Persistence
|
||||
import { PrismaService } from './persistence/prisma/prisma.service';
|
||||
|
|
@ -16,9 +18,18 @@ import { PrismaService } from './persistence/prisma/prisma.service';
|
|||
import { EventPublisherService } from './messaging/kafka/event-publisher.service';
|
||||
import { EventConsumerService } from './messaging/kafka/event-consumer.service';
|
||||
|
||||
// External Services
|
||||
import { BackupClientService } from './external/backup';
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
imports: [ConfigModule],
|
||||
imports: [
|
||||
ConfigModule,
|
||||
HttpModule.register({
|
||||
timeout: 30000,
|
||||
maxRedirects: 5,
|
||||
}),
|
||||
],
|
||||
providers: [
|
||||
// Prisma (用于缓存公钥和 delegate share)
|
||||
PrismaService,
|
||||
|
|
@ -26,11 +37,15 @@ import { EventConsumerService } from './messaging/kafka/event-consumer.service';
|
|||
// Kafka (事件发布和消费)
|
||||
EventPublisherService,
|
||||
EventConsumerService,
|
||||
|
||||
// External Services
|
||||
BackupClientService,
|
||||
],
|
||||
exports: [
|
||||
PrismaService,
|
||||
EventPublisherService,
|
||||
EventConsumerService,
|
||||
BackupClientService,
|
||||
],
|
||||
})
|
||||
export class InfrastructureModule {}
|
||||
|
|
|
|||
Loading…
Reference in New Issue