UNPKG

n8n

Version:

n8n Workflow Automation Tool

433 lines 22.4 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 (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __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 __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.WorkflowsController = void 0; const express_1 = __importDefault(require("express")); const uuid_1 = require("uuid"); const axios_1 = __importDefault(require("axios")); const Db = __importStar(require("../Db")); const ResponseHelper = __importStar(require("../ResponseHelper")); const WorkflowHelpers = __importStar(require("../WorkflowHelpers")); const config_1 = __importDefault(require("../config")); const decorators_1 = require("../decorators"); const SharedWorkflow_1 = require("../databases/entities/SharedWorkflow"); const WorkflowEntity_1 = require("../databases/entities/WorkflowEntity"); const sharedWorkflow_repository_1 = require("../databases/repositories/sharedWorkflow.repository"); const tag_repository_1 = require("../databases/repositories/tag.repository"); const workflow_repository_1 = require("../databases/repositories/workflow.repository"); const GenericHelpers_1 = require("../GenericHelpers"); const ExternalHooks_1 = require("../ExternalHooks"); const workflow_service_1 = require("./workflow.service"); const License_1 = require("../License"); const utils = __importStar(require("../utils")); const middlewares_1 = require("../middlewares"); const tag_service_1 = require("../services/tag.service"); const workflowHistory_service_ee_1 = require("./workflowHistory/workflowHistory.service.ee"); const Logger_1 = require("../Logger"); 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 internal_server_error_1 = require("../errors/response-errors/internal-server.error"); const forbidden_error_1 = require("../errors/response-errors/forbidden.error"); const naming_service_1 = require("../services/naming.service"); const userOnboarding_service_1 = require("../services/userOnboarding.service"); const credentials_service_1 = require("../credentials/credentials.service"); const workflow_service_ee_1 = require("./workflow.service.ee"); const workflowExecution_service_1 = require("./workflowExecution.service"); const email_1 = require("../UserManagement/email"); const project_repository_1 = require("../databases/repositories/project.repository"); const project_service_1 = require("../services/project.service"); const n8n_workflow_1 = require("n8n-workflow"); const typeorm_1 = require("@n8n/typeorm"); const projectRelation_repository_1 = require("../databases/repositories/projectRelation.repository"); const zod_1 = require("zod"); const event_service_1 = require("../events/event.service"); const config_2 = require("@n8n/config"); let WorkflowsController = class WorkflowsController { constructor(logger, externalHooks, tagRepository, enterpriseWorkflowService, workflowHistoryService, tagService, namingService, userOnboardingService, workflowRepository, workflowService, workflowExecutionService, sharedWorkflowRepository, license, mailer, credentialsService, projectRepository, projectService, projectRelationRepository, eventService, globalConfig) { this.logger = logger; this.externalHooks = externalHooks; this.tagRepository = tagRepository; this.enterpriseWorkflowService = enterpriseWorkflowService; this.workflowHistoryService = workflowHistoryService; this.tagService = tagService; this.namingService = namingService; this.userOnboardingService = userOnboardingService; 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; } async create(req) { delete req.body.id; delete req.body.shared; const newWorkflow = new WorkflowEntity_1.WorkflowEntity(); Object.assign(newWorkflow, req.body); newWorkflow.versionId = (0, uuid_1.v4)(); await (0, GenericHelpers_1.validateEntity)(newWorkflow); await this.externalHooks.run('workflow.create', [newWorkflow]); const { tags: tagIds } = req.body; if ((tagIds === null || tagIds === void 0 ? void 0 : tagIds.length) && !config_1.default.getEnv('workflowTagsDisabled')) { 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'); } } let project; const savedWorkflow = await Db.transaction(async (transactionManager) => { const workflow = await transactionManager.save(newWorkflow); const { projectId } = 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.ApplicationError('No personal project found'); } const newSharedWorkflow = this.sharedWorkflowRepository.create({ role: 'workflow:owner', projectId: project.id, workflow, }); await transactionManager.save(newSharedWorkflow); return await this.sharedWorkflowRepository.findWorkflowForUser(workflow.id, req.user, ['workflow:read'], { em: transactionManager, includeTags: 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 && !config_1.default.getEnv('workflowTagsDisabled') && 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); res.json({ count, data }); } catch (maybeError) { const error = utils.toError(maybeError); ResponseHelper.reportError(error); ResponseHelper.sendErrorResponse(res, error); } } async getNewName(req) { var _a, _b; const requestedName = (_a = req.query.name) !== null && _a !== void 0 ? _a : this.globalConfig.workflows.defaultName; const name = await this.namingService.getUniqueWorkflowName(requestedName); const onboardingFlowEnabled = !this.globalConfig.workflows.onboardingFlowDisabled && !((_b = req.user.settings) === null || _b === void 0 ? void 0 : _b.isOnboarded) && (await this.userOnboardingService.isBelowThreshold(req.user)); return { name, onboardingFlowEnabled }; } async getFromUrl(req) { if (req.query.url === undefined) { throw new bad_request_error_1.BadRequestError('The parameter "url" is missing!'); } if (!/^http[s]?:\/\/.*\.json$/i.exec(req.query.url)) { throw new bad_request_error_1.BadRequestError('The parameter "url" is not valid! It does not seem to be a URL pointing to a n8n workflow JSON file.'); } let workflowData; try { const { data } = await axios_1.default.get(req.query.url); workflowData = data; } catch (error) { throw new bad_request_error_1.BadRequestError('The URL does not point to valid JSON file!'); } if ((workflowData === null || workflowData === void 0 ? void 0 : 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 (!config_1.default.getEnv('workflowTagsDisabled')) { relations.tags = true; } const workflow = await this.sharedWorkflowRepository.findWorkflowForUser(workflowId, req.user, ['workflow:read'], { includeTags: !config_1.default.getEnv('workflowTagsDisabled') }); 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.sharedWorkflowRepository.findWorkflowForUser(workflowId, req.user, ['workflow:read'], { includeTags: !config_1.default.getEnv('workflowTagsDisabled') }); if (!workflow) { this.logger.verbose('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 WorkflowEntity_1.WorkflowEntity(); const { tags, ...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, isSharingEnabled ? forceSave : true); const scopes = await this.workflowService.getWorkflowScopes(req.user, workflowId); return { ...updatedWorkflow, scopes }; } async delete(req) { const { workflowId } = req.params; const workflow = await this.workflowService.delete(req.user, workflowId); if (!workflow) { this.logger.verbose('User attempted to delete a workflow without permissions', { workflowId, userId: req.user.id, }); throw new bad_request_error_1.BadRequestError('Could not delete the workflow - you can only remove workflows owned by you'); } return true; } async runManually(req) { if (!req.body.workflowData.id) { throw new n8n_workflow_1.ApplicationError('You cannot execute a workflow without an ID', { level: 'warning', }); } if (req.params.workflowId !== req.body.workflowData.id) { throw new n8n_workflow_1.ApplicationError('Workflow ID in body does not match workflow ID in URL', { level: 'warning', }); } 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']); } async share(req) { if (!this.license.isSharingEnabled()) throw new not_found_error_1.NotFoundError('Route not found'); 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.sharedWorkflowRepository.findWorkflowForUser(workflowId, req.user, [ 'workflow:share', ]); if (!workflow) { throw new forbidden_error_1.ForbiddenError(); } let newShareeIds = []; await Db.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(SharedWorkflow_1.SharedWorkflow, { workflowId, projectId: (0, typeorm_1.In)(toUnshare), }); await this.enterpriseWorkflowService.shareWithProjects(workflow, 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) { const body = zod_1.z.object({ destinationProjectId: zod_1.z.string() }).parse(req.body); return await this.enterpriseWorkflowService.transferOne(req.user, req.params.workflowId, body.destinationProjectId); } }; 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'), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __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'), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], WorkflowsController.prototype, "delete", null); __decorate([ (0, decorators_1.Post)('/:workflowId/run'), (0, decorators_1.ProjectScope)('workflow:execute'), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], WorkflowsController.prototype, "runManually", null); __decorate([ (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'), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], WorkflowsController.prototype, "transfer", null); exports.WorkflowsController = WorkflowsController = __decorate([ (0, decorators_1.RestController)('/workflows'), __metadata("design:paramtypes", [Logger_1.Logger, ExternalHooks_1.ExternalHooks, tag_repository_1.TagRepository, workflow_service_ee_1.EnterpriseWorkflowService, workflowHistory_service_ee_1.WorkflowHistoryService, tag_service_1.TagService, naming_service_1.NamingService, userOnboarding_service_1.UserOnboardingService, workflow_repository_1.WorkflowRepository, workflow_service_1.WorkflowService, workflowExecution_service_1.WorkflowExecutionService, sharedWorkflow_repository_1.SharedWorkflowRepository, License_1.License, email_1.UserManagementMailer, credentials_service_1.CredentialsService, project_repository_1.ProjectRepository, project_service_1.ProjectService, projectRelation_repository_1.ProjectRelationRepository, event_service_1.EventService, config_2.GlobalConfig]) ], WorkflowsController); //# sourceMappingURL=workflows.controller.js.map