michangarrito/apps/backend/dist/modules/integrations/services/tenant-integrations.service.js
rckrdmrd 48dea7a5d0 feat: Initial commit - michangarrito
Marketplace móvil para negocios locales mexicanos.

Estructura inicial:
- apps/backend (NestJS API)
- apps/frontend (React Web)
- apps/mobile (Expo/React Native)
- apps/mcp-server (Claude MCP Server)
- apps/whatsapp-service (WhatsApp Business API)
- database/ (PostgreSQL DDL)
- docs/ (Documentación)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 04:41:02 -06:00

276 lines
13 KiB
JavaScript

"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TenantIntegrationsService = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const config_1 = require("@nestjs/config");
const tenant_integration_credential_entity_1 = require("../entities/tenant-integration-credential.entity");
const tenant_whatsapp_number_entity_1 = require("../entities/tenant-whatsapp-number.entity");
const tenant_entity_1 = require("../../auth/entities/tenant.entity");
let TenantIntegrationsService = class TenantIntegrationsService {
constructor(credentialRepo, whatsappNumberRepo, tenantRepo, configService) {
this.credentialRepo = credentialRepo;
this.whatsappNumberRepo = whatsappNumberRepo;
this.tenantRepo = tenantRepo;
this.configService = configService;
}
async getWhatsAppCredentials(tenantId) {
const tenantCredential = await this.credentialRepo.findOne({
where: {
tenantId,
integrationType: tenant_integration_credential_entity_1.IntegrationType.WHATSAPP,
provider: tenant_integration_credential_entity_1.IntegrationProvider.META,
isActive: true,
},
});
if (tenantCredential && tenantCredential.credentials) {
const creds = tenantCredential.credentials;
if (creds.accessToken && creds.phoneNumberId) {
return {
accessToken: creds.accessToken,
phoneNumberId: creds.phoneNumberId,
businessAccountId: creds.businessAccountId,
verifyToken: creds.verifyToken,
isFromPlatform: false,
tenantId,
};
}
}
return this.getPlatformWhatsAppCredentials();
}
getPlatformWhatsAppCredentials() {
const accessToken = this.configService.get('WHATSAPP_ACCESS_TOKEN');
const phoneNumberId = this.configService.get('WHATSAPP_PHONE_NUMBER_ID');
if (!accessToken || !phoneNumberId) {
throw new common_1.BadRequestException('WhatsApp platform credentials not configured');
}
return {
accessToken,
phoneNumberId,
businessAccountId: this.configService.get('WHATSAPP_BUSINESS_ACCOUNT_ID'),
verifyToken: this.configService.get('WHATSAPP_VERIFY_TOKEN'),
isFromPlatform: true,
};
}
async resolveTenantFromPhoneNumberId(phoneNumberId) {
const whatsappNumber = await this.whatsappNumberRepo.findOne({
where: { phoneNumberId, isActive: true },
});
if (whatsappNumber) {
return whatsappNumber.tenantId;
}
const platformPhoneNumberId = this.configService.get('WHATSAPP_PHONE_NUMBER_ID');
if (phoneNumberId === platformPhoneNumberId) {
return null;
}
const credential = await this.credentialRepo.findOne({
where: {
integrationType: tenant_integration_credential_entity_1.IntegrationType.WHATSAPP,
isActive: true,
},
});
if (credential) {
const creds = credential.credentials;
if (creds.phoneNumberId === phoneNumberId) {
return credential.tenantId;
}
}
return null;
}
async getLLMConfig(tenantId) {
const tenant = await this.tenantRepo.findOne({ where: { id: tenantId } });
const preferredProvider = tenant?.preferredLlmProvider || 'openai';
const tenantCredential = await this.credentialRepo.findOne({
where: {
tenantId,
integrationType: tenant_integration_credential_entity_1.IntegrationType.LLM,
isActive: true,
},
});
if (tenantCredential && tenantCredential.credentials) {
const creds = tenantCredential.credentials;
const config = tenantCredential.config;
if (creds.apiKey) {
return {
apiKey: creds.apiKey,
provider: tenantCredential.provider,
model: config.model || this.getDefaultModel(tenantCredential.provider),
maxTokens: config.maxTokens || 1000,
temperature: config.temperature || 0.7,
baseUrl: config.baseUrl || this.getDefaultBaseUrl(tenantCredential.provider),
systemPrompt: config.systemPrompt,
isFromPlatform: false,
tenantId,
};
}
}
return this.getPlatformLLMConfig();
}
getPlatformLLMConfig() {
const apiKey = this.configService.get('OPENAI_API_KEY') ||
this.configService.get('LLM_API_KEY');
const provider = this.configService.get('LLM_PROVIDER', 'openai');
if (!apiKey) {
throw new common_1.BadRequestException('LLM platform credentials not configured');
}
return {
apiKey,
provider,
model: this.configService.get('LLM_MODEL', 'gpt-4o-mini'),
maxTokens: parseInt(this.configService.get('LLM_MAX_TOKENS', '1000')),
temperature: parseFloat(this.configService.get('LLM_TEMPERATURE', '0.7')),
baseUrl: this.configService.get('LLM_BASE_URL', 'https://api.openai.com/v1'),
isFromPlatform: true,
};
}
getDefaultModel(provider) {
const defaults = {
[tenant_integration_credential_entity_1.IntegrationProvider.OPENAI]: 'gpt-4o-mini',
[tenant_integration_credential_entity_1.IntegrationProvider.OPENROUTER]: 'anthropic/claude-3-haiku',
[tenant_integration_credential_entity_1.IntegrationProvider.ANTHROPIC]: 'claude-3-haiku-20240307',
[tenant_integration_credential_entity_1.IntegrationProvider.OLLAMA]: 'llama2',
[tenant_integration_credential_entity_1.IntegrationProvider.AZURE_OPENAI]: 'gpt-4o-mini',
};
return defaults[provider] || 'gpt-4o-mini';
}
getDefaultBaseUrl(provider) {
const defaults = {
[tenant_integration_credential_entity_1.IntegrationProvider.OPENAI]: 'https://api.openai.com/v1',
[tenant_integration_credential_entity_1.IntegrationProvider.OPENROUTER]: 'https://openrouter.ai/api/v1',
[tenant_integration_credential_entity_1.IntegrationProvider.ANTHROPIC]: 'https://api.anthropic.com/v1',
[tenant_integration_credential_entity_1.IntegrationProvider.OLLAMA]: 'http://localhost:11434/v1',
};
return defaults[provider] || 'https://api.openai.com/v1';
}
async upsertCredential(tenantId, integrationType, provider, credentials, config, userId) {
let credential = await this.credentialRepo.findOne({
where: { tenantId, integrationType, provider },
});
if (credential) {
credential.credentials = credentials;
credential.config = config || credential.config;
credential.updatedBy = userId;
credential.isVerified = false;
}
else {
credential = this.credentialRepo.create({
tenantId,
integrationType,
provider,
credentials,
config: config || {},
createdBy: userId,
updatedBy: userId,
});
}
return this.credentialRepo.save(credential);
}
async getCredentials(tenantId) {
return this.credentialRepo.find({
where: { tenantId },
order: { integrationType: 'ASC', provider: 'ASC' },
});
}
async getCredential(tenantId, integrationType, provider) {
return this.credentialRepo.findOne({
where: { tenantId, integrationType, provider },
});
}
async deleteCredential(tenantId, integrationType, provider) {
await this.credentialRepo.delete({ tenantId, integrationType, provider });
}
async toggleCredential(tenantId, integrationType, provider, isActive) {
const credential = await this.credentialRepo.findOne({
where: { tenantId, integrationType, provider },
});
if (!credential) {
throw new common_1.NotFoundException('Credential not found');
}
credential.isActive = isActive;
return this.credentialRepo.save(credential);
}
async getIntegrationStatus(tenantId) {
const credentials = await this.getCredentials(tenantId);
const tenant = await this.tenantRepo.findOne({ where: { id: tenantId } });
const whatsappCred = credentials.find((c) => c.integrationType === tenant_integration_credential_entity_1.IntegrationType.WHATSAPP && c.isActive);
const llmCred = credentials.find((c) => c.integrationType === tenant_integration_credential_entity_1.IntegrationType.LLM && c.isActive);
const stripeCred = credentials.find((c) => c.integrationType === tenant_integration_credential_entity_1.IntegrationType.STRIPE && c.isActive);
const mercadopagoCred = credentials.find((c) => c.integrationType === tenant_integration_credential_entity_1.IntegrationType.MERCADOPAGO && c.isActive);
const clipCred = credentials.find((c) => c.integrationType === tenant_integration_credential_entity_1.IntegrationType.CLIP && c.isActive);
return {
whatsapp: {
configured: !!whatsappCred,
usesPlatformNumber: tenant?.usesPlatformNumber ?? true,
provider: whatsappCred?.provider || 'meta',
isVerified: whatsappCred?.isVerified ?? false,
},
llm: {
configured: !!llmCred,
usesPlatformDefault: !llmCred,
provider: llmCred?.provider || tenant?.preferredLlmProvider || 'openai',
model: llmCred?.config?.model || 'gpt-4o-mini',
isVerified: llmCred?.isVerified ?? false,
},
payments: {
stripe: {
configured: !!stripeCred,
isVerified: stripeCred?.isVerified ?? false,
},
mercadopago: {
configured: !!mercadopagoCred,
isVerified: mercadopagoCred?.isVerified ?? false,
},
clip: {
configured: !!clipCred,
isVerified: clipCred?.isVerified ?? false,
},
},
};
}
async registerWhatsAppNumber(tenantId, phoneNumberId, phoneNumber, displayName, isPlatformNumber = false) {
let mapping = await this.whatsappNumberRepo.findOne({
where: { phoneNumberId },
});
if (mapping) {
mapping.tenantId = tenantId;
mapping.phoneNumber = phoneNumber || mapping.phoneNumber;
mapping.displayName = displayName || mapping.displayName;
mapping.isPlatformNumber = isPlatformNumber;
}
else {
mapping = this.whatsappNumberRepo.create({
tenantId,
phoneNumberId,
phoneNumber,
displayName,
isPlatformNumber,
});
}
return this.whatsappNumberRepo.save(mapping);
}
};
exports.TenantIntegrationsService = TenantIntegrationsService;
exports.TenantIntegrationsService = TenantIntegrationsService = __decorate([
(0, common_1.Injectable)(),
__param(0, (0, typeorm_1.InjectRepository)(tenant_integration_credential_entity_1.TenantIntegrationCredential)),
__param(1, (0, typeorm_1.InjectRepository)(tenant_whatsapp_number_entity_1.TenantWhatsAppNumber)),
__param(2, (0, typeorm_1.InjectRepository)(tenant_entity_1.Tenant)),
__metadata("design:paramtypes", [typeorm_2.Repository,
typeorm_2.Repository,
typeorm_2.Repository,
config_1.ConfigService])
], TenantIntegrationsService);
//# sourceMappingURL=tenant-integrations.service.js.map