michangarrito-backend-v2/src/modules/integrations/controllers/integrations.controller.ts
rckrdmrd 59e6ea4ab6 Migración desde michangarrito/backend - Estándar multi-repo v2
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 08:12:07 -06:00

264 lines
7.7 KiB
TypeScript

import {
Controller,
Get,
Post,
Put,
Delete,
Body,
Param,
UseGuards,
Request,
HttpCode,
HttpStatus,
} from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth } from '@nestjs/swagger';
import { JwtAuthGuard } from '../../auth/guards/jwt-auth.guard';
import { TenantIntegrationsService } from '../services/tenant-integrations.service';
import {
UpsertWhatsAppCredentialsDto,
UpsertLLMCredentialsDto,
CreateIntegrationCredentialDto,
IntegrationCredentialResponseDto,
IntegrationStatusResponseDto,
} from '../dto/integration-credentials.dto';
import {
IntegrationType,
IntegrationProvider,
} from '../entities/tenant-integration-credential.entity';
@ApiTags('Integrations')
@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
@Controller('integrations')
export class IntegrationsController {
constructor(private readonly integrationsService: TenantIntegrationsService) {}
// =========================================================================
// STATUS
// =========================================================================
@Get('status')
@ApiOperation({ summary: 'Obtener estado de todas las integraciones del tenant' })
@ApiResponse({ status: 200, type: IntegrationStatusResponseDto })
async getStatus(@Request() req): Promise<IntegrationStatusResponseDto> {
return this.integrationsService.getIntegrationStatus(req.user.tenantId);
}
// =========================================================================
// WHATSAPP
// =========================================================================
@Get('whatsapp')
@ApiOperation({ summary: 'Obtener configuración de WhatsApp' })
async getWhatsAppConfig(@Request() req) {
const credential = await this.integrationsService.getCredential(
req.user.tenantId,
IntegrationType.WHATSAPP,
IntegrationProvider.META,
);
if (!credential) {
return {
configured: false,
usesPlatformNumber: true,
message: 'Usando número de plataforma compartido',
};
}
return {
configured: true,
usesPlatformNumber: false,
isVerified: credential.isVerified,
lastVerifiedAt: credential.lastVerifiedAt,
// No exponer credenciales sensibles
hasAccessToken: !!credential.credentials?.['accessToken'],
phoneNumberId: credential.credentials?.['phoneNumberId'],
};
}
@Put('whatsapp')
@ApiOperation({ summary: 'Configurar credenciales de WhatsApp propias' })
async upsertWhatsAppCredentials(
@Request() req,
@Body() dto: UpsertWhatsAppCredentialsDto,
) {
const credential = await this.integrationsService.upsertCredential(
req.user.tenantId,
IntegrationType.WHATSAPP,
IntegrationProvider.META,
{
accessToken: dto.credentials.accessToken,
phoneNumberId: dto.credentials.phoneNumberId,
businessAccountId: dto.credentials.businessAccountId,
verifyToken: dto.credentials.verifyToken,
},
{},
req.user.sub,
);
// Registrar el número de WhatsApp para resolución en webhooks
if (dto.credentials.phoneNumberId) {
await this.integrationsService.registerWhatsAppNumber(
req.user.tenantId,
dto.credentials.phoneNumberId,
dto.phoneNumber,
dto.displayName,
);
}
return {
success: true,
message: 'Credenciales de WhatsApp configuradas',
id: credential.id,
};
}
@Delete('whatsapp')
@HttpCode(HttpStatus.NO_CONTENT)
@ApiOperation({ summary: 'Eliminar credenciales de WhatsApp (volver a usar plataforma)' })
async deleteWhatsAppCredentials(@Request() req) {
await this.integrationsService.deleteCredential(
req.user.tenantId,
IntegrationType.WHATSAPP,
IntegrationProvider.META,
);
}
// =========================================================================
// LLM
// =========================================================================
@Get('llm')
@ApiOperation({ summary: 'Obtener configuración de LLM' })
async getLLMConfig(@Request() req) {
const credentials = await this.integrationsService.getCredentials(req.user.tenantId);
const llmCred = credentials.find(
(c) => c.integrationType === IntegrationType.LLM && c.isActive,
);
if (!llmCred) {
return {
configured: false,
usesPlatformDefault: true,
message: 'Usando configuración LLM de plataforma',
};
}
return {
configured: true,
usesPlatformDefault: false,
provider: llmCred.provider,
isVerified: llmCred.isVerified,
config: {
model: llmCred.config?.['model'],
maxTokens: llmCred.config?.['maxTokens'],
temperature: llmCred.config?.['temperature'],
hasSystemPrompt: !!llmCred.config?.['systemPrompt'],
},
// No exponer API key
hasApiKey: !!llmCred.credentials?.['apiKey'],
};
}
@Put('llm')
@ApiOperation({ summary: 'Configurar credenciales de LLM propias' })
async upsertLLMCredentials(@Request() req, @Body() dto: UpsertLLMCredentialsDto) {
const credential = await this.integrationsService.upsertCredential(
req.user.tenantId,
IntegrationType.LLM,
dto.provider,
{ apiKey: dto.credentials.apiKey },
dto.config || {},
req.user.sub,
);
return {
success: true,
message: 'Credenciales de LLM configuradas',
id: credential.id,
provider: dto.provider,
};
}
@Delete('llm/:provider')
@HttpCode(HttpStatus.NO_CONTENT)
@ApiOperation({ summary: 'Eliminar credenciales de LLM (volver a usar plataforma)' })
async deleteLLMCredentials(
@Request() req,
@Param('provider') provider: IntegrationProvider,
) {
await this.integrationsService.deleteCredential(
req.user.tenantId,
IntegrationType.LLM,
provider,
);
}
// =========================================================================
// GENERIC CRUD
// =========================================================================
@Get('credentials')
@ApiOperation({ summary: 'Obtener todas las credenciales del tenant' })
async getAllCredentials(@Request() req): Promise<IntegrationCredentialResponseDto[]> {
const credentials = await this.integrationsService.getCredentials(req.user.tenantId);
return credentials.map((c) => ({
id: c.id,
integrationType: c.integrationType,
provider: c.provider,
hasCredentials: !!c.credentials && Object.keys(c.credentials).length > 0,
isActive: c.isActive,
isVerified: c.isVerified,
lastVerifiedAt: c.lastVerifiedAt,
verificationError: c.verificationError,
config: c.config,
createdAt: c.createdAt,
updatedAt: c.updatedAt,
}));
}
@Post('credentials')
@ApiOperation({ summary: 'Crear credencial de integración genérica' })
async createCredential(
@Request() req,
@Body() dto: CreateIntegrationCredentialDto,
) {
const credential = await this.integrationsService.upsertCredential(
req.user.tenantId,
dto.integrationType,
dto.provider,
dto.credentials,
dto.config,
req.user.sub,
);
return {
success: true,
message: 'Credencial creada',
id: credential.id,
};
}
@Put('credentials/:type/:provider/toggle')
@ApiOperation({ summary: 'Activar/desactivar una credencial' })
async toggleCredential(
@Request() req,
@Param('type') type: IntegrationType,
@Param('provider') provider: IntegrationProvider,
@Body() body: { isActive: boolean },
) {
const credential = await this.integrationsService.toggleCredential(
req.user.tenantId,
type,
provider,
body.isActive,
);
return {
success: true,
isActive: credential.isActive,
};
}
}