n8n
Version:
n8n Workflow Automation Tool
280 lines • 15.3 kB
JavaScript
;
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