UNPKG

n8n

Version:

n8n Workflow Automation Tool

280 lines • 15.3 kB
"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.AgentIntegrationsController = void 0; const api_types_1 = require("@n8n/api-types"); const decorators_1 = require("@n8n/decorators"); const credentials_service_1 = require("../../credentials/credentials.service"); const bad_request_error_1 = require("../../errors/response-errors/bad-request.error"); const not_found_error_1 = require("../../errors/response-errors/not-found.error"); const agent_integration_persistence_service_1 = require("./agent-integration-persistence.service"); const agent_publish_service_1 = require("./agent-publish.service"); const agent_runnable_state_service_1 = require("./agent-runnable-state.service"); const agent_chat_integration_1 = require("./integrations/agent-chat-integration"); const chat_integration_service_1 = require("./integrations/chat-integration.service"); const channel_integration_recorder_1 = require("./integrations/recording/channel-integration-recorder"); const slack_app_setup_service_1 = require("./integrations/slack-app-setup.service"); const agent_repository_1 = require("./repositories/agent.repository"); let AgentIntegrationsController = class AgentIntegrationsController { constructor(agentIntegrationPersistenceService, agentPublishService, credentialsService, chatIntegrationService, agentRepository, chatIntegrationRegistry, slackAppSetupService, agentRunnableStateService) { this.agentIntegrationPersistenceService = agentIntegrationPersistenceService; this.agentPublishService = agentPublishService; this.credentialsService = credentialsService; this.chatIntegrationService = chatIntegrationService; this.agentRepository = agentRepository; this.chatIntegrationRegistry = chatIntegrationRegistry; this.slackAppSetupService = slackAppSetupService; this.agentRunnableStateService = agentRunnableStateService; } async validateIntegration(dto) { const integrationParseResult = await api_types_1.AgentIntegrationSchema.safeParseAsync(dto); if (!integrationParseResult.success) { throw new bad_request_error_1.BadRequestError(integrationParseResult.error.message); } const integration = integrationParseResult.data; if (integration.type === 'telegram' && !integration.settings) { throw new bad_request_error_1.BadRequestError('Telegram integration settings are required'); } return integration; } async connectIntegration(req, _res, agentId) { const integration = await this.validateIntegration(req.body); const { credentialId } = integration; const agent = await this.agentRepository.findByIdAndProjectId(agentId, req.params.projectId); if (!agent) throw new not_found_error_1.NotFoundError(`Agent "${agentId}" not found`); const usableCredentials = await this.credentialsService.getCredentialsAUserCanUseInAWorkflow(req.user, { projectId: agent.projectId }); const credential = usableCredentials.find((c) => c.id === credentialId); if (!credential) throw new not_found_error_1.NotFoundError(`Credential "${credentialId}" not found`); const integrationImpl = this.chatIntegrationRegistry.require(integration.type); if (!integrationImpl.credentialTypes.includes(credential.type)) { throw new bad_request_error_1.BadRequestError(`${integrationImpl.displayLabel} integrations do not support ${credential.type} credentials`); } await this.agentIntegrationPersistenceService.saveCredentialIntegration(agent, integration, { broadcast: false, }); const { agent: publishedAgent, draftValidation } = await this.agentPublishService.publishAgent(agentId, agent.projectId, req.user, 'channel_connect', undefined, { syncIntegrations: false, ignoreDraftIntegrations: true }); await this.chatIntegrationService.connect(agentId, integration, agent.projectId); await this.chatIntegrationService.broadcastIntegrationChange(agentId, integration, 'connect'); return { status: 'connected', agent: await this.agentRunnableStateService.addRunnableState(publishedAgent, agent.projectId, req.user, draftValidation), }; } async createSlackApp(req, _res, agentId, payload) { return await this.slackAppSetupService.createApp({ projectId: req.params.projectId, agentId, appConfigurationToken: payload.appConfigurationToken, user: req.user, }); } async getSlackAppManifest(req, _res, agentId) { return await this.slackAppSetupService.getManualManifest({ projectId: req.params.projectId, agentId, }); } async handleSlackAppOAuthCallback(req, res, agentId) { const { code, state, error, error_description: errorDescription } = req.query; if (error) { return res.render('oauth-error-callback', { error: { message: error, ...(errorDescription ? { reason: errorDescription } : {}), }, }); } if (!code || !state) { return res.render('oauth-error-callback', { error: { message: 'Insufficient parameters for Slack app setup callback.' }, }); } try { await this.slackAppSetupService.completeInstall({ projectId: req.params.projectId, agentId, code, state, }); return res.render('oauth-callback'); } catch (callbackError) { const message = callbackError instanceof Error ? callbackError.message : 'Slack app setup failed'; return res.render('oauth-error-callback', { error: { message }, }); } } async disconnectIntegration(req, _res, agentId, payload) { const { type, credentialId } = payload; const agent = await this.agentRepository.findByIdAndProjectId(agentId, req.params.projectId); if (!agent) throw new not_found_error_1.NotFoundError(`Agent "${agentId}" not found`); const persistedIntegration = agent.integrations?.find((integration) => integration.type === type && integration.credentialId === credentialId); const parsedIntegration = api_types_1.AgentIntegrationSchema.safeParse({ type, credentialId }); const integration = persistedIntegration ?? (parsedIntegration.success ? parsedIntegration.data : undefined); if (integration) { await this.chatIntegrationService.disconnectChannel(agentId, integration); } else { await this.chatIntegrationService.disconnect(agentId, { type, credentialId }); } await this.agentIntegrationPersistenceService.removeCredentialIntegration(agent, type, credentialId, { broadcast: false }); return { status: 'disconnected' }; } async integrationStatus(req, _res, agentId) { const agent = await this.agentRepository.findByIdAndProjectId(agentId, req.params.projectId); if (!agent) throw new not_found_error_1.NotFoundError(`Agent "${agentId}" not found`); const chatIntegrations = (agent.integrations ?? []) .filter((i) => !(0, api_types_1.isDraftIntegration)(i)) .map((i) => ({ type: i.type, credentialId: i.credentialId, ...('settings' in i ? { settings: i.settings } : {}), })); return { status: chatIntegrations.length > 0 ? 'connected' : 'disconnected', integrations: chatIntegrations, }; } async handleWebhook(req, res) { const { agentId, platform } = req.params; const webhookHandler = this.chatIntegrationService.getWebhookHandler(agentId, platform); if (!webhookHandler) { const integration = this.chatIntegrationRegistry.get(platform); const earlyResponse = integration?.handleUnauthenticatedWebhook?.(req.body); if (earlyResponse) { res.status(earlyResponse.status).json(earlyResponse.body); return; } res.status(404).json({ error: `No active ${platform} integration for agent "${agentId}"` }); return; } const forwardedProto = req.headers['x-forwarded-proto']; const protocol = (Array.isArray(forwardedProto) ? forwardedProto[0] : forwardedProto) ?? req.protocol; const forwardedHost = req.headers['x-forwarded-host']; const host = (Array.isArray(forwardedHost) ? forwardedHost[0] : forwardedHost) ?? req.headers.host ?? 'localhost'; const url = `${protocol}://${host}${req.originalUrl}`; let requestBody; if (req.method !== 'GET' && req.method !== 'HEAD') { const rawBody = req.rawBody; if (rawBody) { requestBody = rawBody.toString('utf-8'); } else if (req.headers['content-type']?.includes('application/json')) { requestBody = JSON.stringify(req.body); } else if (req.headers['content-type']?.includes('application/x-www-form-urlencoded')) { requestBody = new URLSearchParams(req.body).toString(); } else { requestBody = JSON.stringify(req.body); } } const sanitizedHeaders = {}; for (const [key, value] of Object.entries(req.headers)) { if (typeof value === 'string') { sanitizedHeaders[key] = value; } else if (Array.isArray(value)) { sanitizedHeaders[key] = value.join(', '); } } const webRequest = new globalThis.Request(url, { method: req.method, headers: sanitizedHeaders, body: requestBody, }); await channel_integration_recorder_1.channelIntegrationRecorder.recordWebhook(platform, webRequest.clone()); const backgroundTasks = []; const waitUntil = (task) => { backgroundTasks.push(task.catch((error) => { console.warn('[AgentIntegrationsController] Background task failed:', error instanceof Error ? error.message : String(error)); })); }; const webResponse = await webhookHandler(webRequest, { waitUntil }); res.status(webResponse.status); webResponse.headers.forEach((value, key) => { res.setHeader(key, value); }); const body = await webResponse.text(); res.send(body); } }; exports.AgentIntegrationsController = AgentIntegrationsController; __decorate([ (0, decorators_1.Post)('/:agentId/integrations/connect'), (0, decorators_1.ProjectScope)('agent:update'), __param(2, (0, decorators_1.Param)('agentId')), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object, String]), __metadata("design:returntype", Promise) ], AgentIntegrationsController.prototype, "connectIntegration", null); __decorate([ (0, decorators_1.Post)('/:agentId/integrations/slack/app'), (0, decorators_1.ProjectScope)('agent:update'), __param(2, (0, decorators_1.Param)('agentId')), __param(3, decorators_1.Body), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object, String, api_types_1.CreateSlackAgentAppDto]), __metadata("design:returntype", Promise) ], AgentIntegrationsController.prototype, "createSlackApp", null); __decorate([ (0, decorators_1.Get)('/:agentId/integrations/slack/manifest'), (0, decorators_1.ProjectScope)('agent:read'), __param(2, (0, decorators_1.Param)('agentId')), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object, String]), __metadata("design:returntype", Promise) ], AgentIntegrationsController.prototype, "getSlackAppManifest", null); __decorate([ (0, decorators_1.Get)('/:agentId/integrations/slack/oauth/callback', { skipAuth: true, usesTemplates: true }), __param(2, (0, decorators_1.Param)('agentId')), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object, String]), __metadata("design:returntype", Promise) ], AgentIntegrationsController.prototype, "handleSlackAppOAuthCallback", null); __decorate([ (0, decorators_1.Post)('/:agentId/integrations/disconnect'), (0, decorators_1.ProjectScope)('agent:update'), __param(2, (0, decorators_1.Param)('agentId')), __param(3, decorators_1.Body), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object, String, api_types_1.AgentDisconnectIntegrationDto]), __metadata("design:returntype", Promise) ], AgentIntegrationsController.prototype, "disconnectIntegration", null); __decorate([ (0, decorators_1.Get)('/:agentId/integrations/status'), (0, decorators_1.ProjectScope)('agent:read'), __param(2, (0, decorators_1.Param)('agentId')), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object, String]), __metadata("design:returntype", Promise) ], AgentIntegrationsController.prototype, "integrationStatus", null); __decorate([ (0, decorators_1.Post)('/:agentId/webhooks/:platform', { skipAuth: true, allowBots: true }), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", Promise) ], AgentIntegrationsController.prototype, "handleWebhook", null); exports.AgentIntegrationsController = AgentIntegrationsController = __decorate([ (0, decorators_1.RestController)('/projects/:projectId/agents/v2'), __metadata("design:paramtypes", [agent_integration_persistence_service_1.AgentIntegrationPersistenceService, agent_publish_service_1.AgentPublishService, credentials_service_1.CredentialsService, chat_integration_service_1.ChatIntegrationService, agent_repository_1.AgentRepository, agent_chat_integration_1.ChatIntegrationRegistry, slack_app_setup_service_1.SlackAppSetupService, agent_runnable_state_service_1.AgentRunnableStateService]) ], AgentIntegrationsController); //# sourceMappingURL=agent-integrations.controller.js.map