UNPKG

n8n

Version:

n8n Workflow Automation Tool

476 lines 24.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); 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 __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); 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); } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.WorkflowsController = void 0; const api_types_1 = require("@n8n/api-types"); const backend_common_1 = require("@n8n/backend-common"); const config_1 = require("@n8n/config"); const db_1 = require("@n8n/db"); const decorators_1 = require("@n8n/decorators"); const typeorm_1 = require("@n8n/typeorm"); const axios_1 = __importDefault(require("axios")); const express_1 = __importDefault(require("express")); const n8n_workflow_1 = require("n8n-workflow"); const uuid_1 = require("uuid"); const bad_request_error_1 = require("../errors/response-errors/bad-request.error"); const forbidden_error_1 = require("../errors/response-errors/forbidden.error"); const internal_server_error_1 = require("../errors/response-errors/internal-server.error"); const not_found_error_1 = require("../errors/response-errors/not-found.error"); const event_service_1 = require("../events/event.service"); const external_hooks_1 = require("../external-hooks"); const generic_helpers_1 = require("../generic-helpers"); const license_1 = require("../license"); const middlewares_1 = require("../middlewares"); const ResponseHelper = __importStar(require("../response-helper")); const folder_service_1 = require("../services/folder.service"); const naming_service_1 = require("../services/naming.service"); const project_service_ee_1 = require("../services/project.service.ee"); const tag_service_1 = require("../services/tag.service"); const email_1 = require("../user-management/email"); const utils = __importStar(require("../utils")); const WorkflowHelpers = __importStar(require("../workflow-helpers")); const workflow_execution_service_1 = require("./workflow-execution.service"); const workflow_finder_service_1 = require("./workflow-finder.service"); const workflow_history_service_ee_1 = require("./workflow-history.ee/workflow-history.service.ee"); const workflow_service_1 = require("./workflow.service"); const workflow_service_ee_1 = require("./workflow.service.ee"); const credentials_service_1 = require("../credentials/credentials.service"); let WorkflowsController = class WorkflowsController { constructor(logger, externalHooks, tagRepository, enterpriseWorkflowService, workflowHistoryService, tagService, namingService, workflowRepository, workflowService, workflowExecutionService, sharedWorkflowRepository, license, mailer, credentialsService, projectRepository, projectService, projectRelationRepository, eventService, globalConfig, folderService, workflowFinderService) { this.logger = logger; this.externalHooks = externalHooks; this.tagRepository = tagRepository; this.enterpriseWorkflowService = enterpriseWorkflowService; this.workflowHistoryService = workflowHistoryService; this.tagService = tagService; this.namingService = namingService; this.workflowRepository = workflowRepository; this.workflowService = workflowService; this.workflowExecutionService = workflowExecutionService; this.sharedWorkflowRepository = sharedWorkflowRepository; this.license = license; this.mailer = mailer; this.credentialsService = credentialsService; this.projectRepository = projectRepository; this.projectService = projectService; this.projectRelationRepository = projectRelationRepository; this.eventService = eventService; this.globalConfig = globalConfig; this.folderService = folderService; this.workflowFinderService = workflowFinderService; } async create(req) { delete req.body.id; delete req.body.shared; const newWorkflow = new db_1.WorkflowEntity(); Object.assign(newWorkflow, req.body); newWorkflow.versionId = (0, uuid_1.v4)(); await (0, generic_helpers_1.validateEntity)(newWorkflow); await this.externalHooks.run('workflow.create', [newWorkflow]); const { tags: tagIds } = req.body; if (tagIds?.length && !this.globalConfig.tags.disabled) { newWorkflow.tags = await this.tagRepository.findMany(tagIds); } await WorkflowHelpers.replaceInvalidCredentials(newWorkflow); WorkflowHelpers.addNodeIds(newWorkflow); if (this.license.isSharingEnabled()) { const allCredentials = await this.credentialsService.getMany(req.user); try { this.enterpriseWorkflowService.validateCredentialPermissionsToUser(newWorkflow, allCredentials); } catch (error) { throw new bad_request_error_1.BadRequestError('The workflow you are trying to save contains credentials that are not shared with you'); } } const { manager: dbManager } = this.projectRepository; let project; const savedWorkflow = await dbManager.transaction(async (transactionManager) => { const workflow = await transactionManager.save(newWorkflow); const { projectId, parentFolderId } = req.body; project = projectId === undefined ? await this.projectRepository.getPersonalProjectForUser(req.user.id, transactionManager) : await this.projectService.getProjectWithScope(req.user, projectId, ['workflow:create'], transactionManager); if (typeof projectId === 'string' && project === null) { throw new bad_request_error_1.BadRequestError("You don't have the permissions to save the workflow in this project."); } if (project === null) { throw new n8n_workflow_1.UnexpectedError('No personal project found'); } if (parentFolderId) { try { const parentFolder = await this.folderService.findFolderInProjectOrFail(parentFolderId, project.id, transactionManager); await transactionManager.update(db_1.WorkflowEntity, { id: workflow.id }, { parentFolder }); } catch { } } const newSharedWorkflow = this.sharedWorkflowRepository.create({ role: 'workflow:owner', projectId: project.id, workflow, }); await transactionManager.save(newSharedWorkflow); return await this.workflowFinderService.findWorkflowForUser(workflow.id, req.user, ['workflow:read'], { em: transactionManager, includeTags: true, includeParentFolder: true }); }); if (!savedWorkflow) { this.logger.error('Failed to create workflow', { userId: req.user.id }); throw new internal_server_error_1.InternalServerError('Failed to save workflow'); } await this.workflowHistoryService.saveVersion(req.user, savedWorkflow, savedWorkflow.id); if (tagIds && !this.globalConfig.tags.disabled && savedWorkflow.tags) { savedWorkflow.tags = this.tagService.sortByRequestOrder(savedWorkflow.tags, { requestOrder: tagIds, }); } const savedWorkflowWithMetaData = this.enterpriseWorkflowService.addOwnerAndSharings(savedWorkflow); delete savedWorkflowWithMetaData.shared; await this.externalHooks.run('workflow.afterCreate', [savedWorkflow]); this.eventService.emit('workflow-created', { user: req.user, workflow: newWorkflow, publicApi: false, projectId: project.id, projectType: project.type, }); const scopes = await this.workflowService.getWorkflowScopes(req.user, savedWorkflow.id); return { ...savedWorkflowWithMetaData, scopes }; } async getAll(req, res) { try { const { workflows: data, count } = await this.workflowService.getMany(req.user, req.listQueryOptions, !!req.query.includeScopes, !!req.query.includeFolders, !!req.query.onlySharedWithMe); res.json({ count, data }); } catch (maybeError) { const error = utils.toError(maybeError); ResponseHelper.reportError(error); ResponseHelper.sendErrorResponse(res, error); } } async getNewName(req) { const requestedName = req.query.name ?? this.globalConfig.workflows.defaultName; const name = await this.namingService.getUniqueWorkflowName(requestedName); return { name }; } async getFromUrl(_req, _res, query) { let workflowData; try { const { data } = await axios_1.default.get(query.url); workflowData = data; } catch (error) { throw new bad_request_error_1.BadRequestError('The URL does not point to valid JSON file!'); } if (workflowData?.nodes === undefined || !Array.isArray(workflowData.nodes) || workflowData.connections === undefined || typeof workflowData.connections !== 'object' || Array.isArray(workflowData.connections)) { throw new bad_request_error_1.BadRequestError('The data in the file does not seem to be a n8n workflow JSON file!'); } return workflowData; } async getWorkflow(req) { const { workflowId } = req.params; if (this.license.isSharingEnabled()) { const relations = { shared: { project: { projectRelations: true, }, }, }; if (!this.globalConfig.tags.disabled) { relations.tags = true; } const workflow = await this.workflowFinderService.findWorkflowForUser(workflowId, req.user, ['workflow:read'], { includeTags: !this.globalConfig.tags.disabled, includeParentFolder: true }); if (!workflow) { throw new not_found_error_1.NotFoundError(`Workflow with ID "${workflowId}" does not exist`); } const enterpriseWorkflowService = this.enterpriseWorkflowService; const workflowWithMetaData = enterpriseWorkflowService.addOwnerAndSharings(workflow); await enterpriseWorkflowService.addCredentialsToWorkflow(workflowWithMetaData, req.user); delete workflowWithMetaData.shared; const scopes = await this.workflowService.getWorkflowScopes(req.user, workflowId); return { ...workflowWithMetaData, scopes }; } const workflow = await this.workflowFinderService.findWorkflowForUser(workflowId, req.user, ['workflow:read'], { includeTags: !this.globalConfig.tags.disabled, includeParentFolder: true }); if (!workflow) { this.logger.warn('User attempted to access a workflow without permissions', { workflowId, userId: req.user.id, }); throw new not_found_error_1.NotFoundError('Could not load the workflow - you can only access workflows owned by you'); } const scopes = await this.workflowService.getWorkflowScopes(req.user, workflowId); return { ...workflow, scopes }; } async update(req) { const { workflowId } = req.params; const forceSave = req.query.forceSave === 'true'; let updateData = new db_1.WorkflowEntity(); const { tags, parentFolderId, ...rest } = req.body; Object.assign(updateData, rest); const isSharingEnabled = this.license.isSharingEnabled(); if (isSharingEnabled) { updateData = await this.enterpriseWorkflowService.preventTampering(updateData, workflowId, req.user); } const updatedWorkflow = await this.workflowService.update(req.user, updateData, workflowId, tags, parentFolderId, isSharingEnabled ? forceSave : true); const scopes = await this.workflowService.getWorkflowScopes(req.user, workflowId); return { ...updatedWorkflow, scopes }; } async delete(req, _res, workflowId) { const workflow = await this.workflowService.delete(req.user, workflowId); if (!workflow) { this.logger.warn('User attempted to delete a workflow without permissions', { workflowId, userId: req.user.id, }); throw new forbidden_error_1.ForbiddenError('Could not delete the workflow - workflow was not found in your projects'); } return true; } async archive(req, _res, workflowId) { const workflow = await this.workflowService.archive(req.user, workflowId); if (!workflow) { this.logger.warn('User attempted to archive a workflow without permissions', { workflowId, userId: req.user.id, }); throw new forbidden_error_1.ForbiddenError('Could not archive the workflow - workflow was not found in your projects'); } return workflow; } async unarchive(req, _res, workflowId) { const workflow = await this.workflowService.unarchive(req.user, workflowId); if (!workflow) { this.logger.warn('User attempted to unarchive a workflow without permissions', { workflowId, userId: req.user.id, }); throw new forbidden_error_1.ForbiddenError('Could not unarchive the workflow - workflow was not found in your projects'); } return workflow; } async runManually(req, _res, query) { if (!req.body.workflowData.id) { throw new n8n_workflow_1.UnexpectedError('You cannot execute a workflow without an ID'); } if (req.params.workflowId !== req.body.workflowData.id) { throw new n8n_workflow_1.UnexpectedError('Workflow ID in body does not match workflow ID in URL'); } if (this.license.isSharingEnabled()) { const workflow = this.workflowRepository.create(req.body.workflowData); const safeWorkflow = await this.enterpriseWorkflowService.preventTampering(workflow, workflow.id, req.user); req.body.workflowData.nodes = safeWorkflow.nodes; } return await this.workflowExecutionService.executeManually(req.body, req.user, req.headers['push-ref'], query.partialExecutionVersion); } async share(req) { const { workflowId } = req.params; const { shareWithIds } = req.body; if (!Array.isArray(shareWithIds) || !shareWithIds.every((userId) => typeof userId === 'string')) { throw new bad_request_error_1.BadRequestError('Bad request'); } const workflow = await this.workflowFinderService.findWorkflowForUser(workflowId, req.user, [ 'workflow:share', ]); if (!workflow) { throw new forbidden_error_1.ForbiddenError(); } let newShareeIds = []; const { manager: dbManager } = this.projectRepository; await dbManager.transaction(async (trx) => { const currentPersonalProjectIDs = workflow.shared .filter((sw) => sw.role === 'workflow:editor') .map((sw) => sw.projectId); const newPersonalProjectIDs = shareWithIds; const toShare = utils.rightDiff([currentPersonalProjectIDs, (id) => id], [newPersonalProjectIDs, (id) => id]); const toUnshare = utils.rightDiff([newPersonalProjectIDs, (id) => id], [currentPersonalProjectIDs, (id) => id]); await trx.delete(db_1.SharedWorkflow, { workflowId, projectId: (0, typeorm_1.In)(toUnshare), }); await this.enterpriseWorkflowService.shareWithProjects(workflow.id, toShare, trx); newShareeIds = toShare; }); this.eventService.emit('workflow-sharing-updated', { workflowId, userIdSharer: req.user.id, userIdList: shareWithIds, }); const projectsRelations = await this.projectRelationRepository.findBy({ projectId: (0, typeorm_1.In)(newShareeIds), role: 'project:personalOwner', }); await this.mailer.notifyWorkflowShared({ sharer: req.user, newShareeIds: projectsRelations.map((pr) => pr.userId), workflow, }); } async transfer(req, _res, workflowId, body) { return await this.enterpriseWorkflowService.transferWorkflow(req.user, workflowId, body.destinationProjectId, body.shareCredentials, body.destinationParentFolderId); } }; exports.WorkflowsController = WorkflowsController; __decorate([ (0, decorators_1.Post)('/'), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], WorkflowsController.prototype, "create", null); __decorate([ (0, decorators_1.Get)('/', { middlewares: middlewares_1.listQueryMiddleware }), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", Promise) ], WorkflowsController.prototype, "getAll", null); __decorate([ (0, decorators_1.Get)('/new'), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], WorkflowsController.prototype, "getNewName", null); __decorate([ (0, decorators_1.Get)('/from-url'), __param(2, decorators_1.Query), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object, api_types_1.ImportWorkflowFromUrlDto]), __metadata("design:returntype", Promise) ], WorkflowsController.prototype, "getFromUrl", null); __decorate([ (0, decorators_1.Get)('/:workflowId'), (0, decorators_1.ProjectScope)('workflow:read'), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], WorkflowsController.prototype, "getWorkflow", null); __decorate([ (0, decorators_1.Patch)('/:workflowId'), (0, decorators_1.ProjectScope)('workflow:update'), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], WorkflowsController.prototype, "update", null); __decorate([ (0, decorators_1.Delete)('/:workflowId'), (0, decorators_1.ProjectScope)('workflow:delete'), __param(2, (0, decorators_1.Param)('workflowId')), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Response, String]), __metadata("design:returntype", Promise) ], WorkflowsController.prototype, "delete", null); __decorate([ (0, decorators_1.Post)('/:workflowId/archive'), (0, decorators_1.ProjectScope)('workflow:delete'), __param(2, (0, decorators_1.Param)('workflowId')), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Response, String]), __metadata("design:returntype", Promise) ], WorkflowsController.prototype, "archive", null); __decorate([ (0, decorators_1.Post)('/:workflowId/unarchive'), (0, decorators_1.ProjectScope)('workflow:delete'), __param(2, (0, decorators_1.Param)('workflowId')), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Response, String]), __metadata("design:returntype", Promise) ], WorkflowsController.prototype, "unarchive", null); __decorate([ (0, decorators_1.Post)('/:workflowId/run'), (0, decorators_1.ProjectScope)('workflow:execute'), __param(2, decorators_1.Query), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object, api_types_1.ManualRunQueryDto]), __metadata("design:returntype", Promise) ], WorkflowsController.prototype, "runManually", null); __decorate([ (0, decorators_1.Licensed)('feat:sharing'), (0, decorators_1.Put)('/:workflowId/share'), (0, decorators_1.ProjectScope)('workflow:share'), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], WorkflowsController.prototype, "share", null); __decorate([ (0, decorators_1.Put)('/:workflowId/transfer'), (0, decorators_1.ProjectScope)('workflow:move'), __param(2, (0, decorators_1.Param)('workflowId')), __param(3, decorators_1.Body), __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object, String, api_types_1.TransferWorkflowBodyDto]), __metadata("design:returntype", Promise) ], WorkflowsController.prototype, "transfer", null); exports.WorkflowsController = WorkflowsController = __decorate([ (0, decorators_1.RestController)('/workflows'), __metadata("design:paramtypes", [backend_common_1.Logger, external_hooks_1.ExternalHooks, db_1.TagRepository, workflow_service_ee_1.EnterpriseWorkflowService, workflow_history_service_ee_1.WorkflowHistoryService, tag_service_1.TagService, naming_service_1.NamingService, db_1.WorkflowRepository, workflow_service_1.WorkflowService, workflow_execution_service_1.WorkflowExecutionService, db_1.SharedWorkflowRepository, license_1.License, email_1.UserManagementMailer, credentials_service_1.CredentialsService, db_1.ProjectRepository, project_service_ee_1.ProjectService, db_1.ProjectRelationRepository, event_service_1.EventService, config_1.GlobalConfig, folder_service_1.FolderService, workflow_finder_service_1.WorkflowFinderService]) ], WorkflowsController); //# sourceMappingURL=workflows.controller.js.map