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 { 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 { 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, }; } }