@atom8n/n8n 2.4.7 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build.tsbuildinfo +1 -1
- package/dist/chat/chat-service.types.d.ts +6 -6
- package/dist/commands/execute-batch.d.ts +2 -2
- package/dist/commands/execute.js +0 -0
- package/dist/commands/import/credentials.d.ts +2 -2
- package/dist/commands/import/credentials.js +0 -0
- package/dist/commands/import/workflow.d.ts +2 -2
- package/dist/commands/ldap/reset.d.ts +2 -2
- package/dist/commands/mcp.d.ts +23 -0
- package/dist/commands/mcp.js +315 -0
- package/dist/commands/run.d.ts +23 -0
- package/dist/commands/run.js +162 -0
- package/dist/controllers/annotation-tags.controller.ee.d.ts +1 -1
- package/dist/controllers/cli.controller.d.ts +26 -0
- package/dist/controllers/cli.controller.js +334 -0
- package/dist/controllers/orchestration.controller.js +0 -0
- package/dist/controllers/users.controller.d.ts +1 -1
- package/dist/environments.ee/source-control/source-control-status.service.ee.d.ts +2 -2
- package/dist/environments.ee/source-control/source-control.controller.ee.d.ts +4 -4
- package/dist/environments.ee/source-control/source-control.service.ee.d.ts +2 -2
- package/dist/environments.ee/source-control/types/source-control-set-branch.js +0 -0
- package/dist/eventbus/event-message-classes/index.js +0 -0
- package/dist/events/relays/workflow-failure-notification.event-relay.d.ts +13 -0
- package/dist/events/relays/workflow-failure-notification.event-relay.js +77 -0
- package/dist/executions/execution-data/types.d.ts +17 -0
- package/dist/executions/execution-data/types.js +2 -0
- package/dist/executions/failed-run-factory.d.ts +4 -0
- package/dist/executions/failed-run-factory.js +71 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +8 -0
- package/dist/interfaces.d.ts +1 -1
- package/dist/license.js +1 -1
- package/dist/middlewares/webview-frame-options.d.ts +2 -0
- package/dist/middlewares/webview-frame-options.js +22 -0
- package/dist/modules/chat-hub/chat-hub-extractor.d.ts +22 -0
- package/dist/modules/chat-hub/chat-hub-extractor.js +63 -0
- package/dist/modules/dynamic-credentials.ee/credential-resolvers/storage/dynamic-credential-user-entry-storage.d.ts +11 -0
- package/dist/modules/dynamic-credentials.ee/credential-resolvers/storage/dynamic-credential-user-entry-storage.js +58 -0
- package/dist/modules/dynamic-credentials.ee/database/entities/dynamic-credential-user-entry.d.ts +12 -0
- package/dist/modules/dynamic-credentials.ee/database/entities/dynamic-credential-user-entry.js +64 -0
- package/dist/modules/dynamic-credentials.ee/database/repositories/dynamic-credential-user-entry.repository.d.ts +5 -0
- package/dist/modules/dynamic-credentials.ee/database/repositories/dynamic-credential-user-entry.repository.js +25 -0
- package/dist/modules/dynamic-credentials.ee/dynamic-credentials.config.d.ts +5 -0
- package/dist/modules/dynamic-credentials.ee/dynamic-credentials.config.js +36 -0
- package/dist/modules/dynamic-credentials.ee/services/dynamic-credential-cors.service.d.ts +12 -0
- package/dist/modules/dynamic-credentials.ee/services/dynamic-credential-cors.service.js +69 -0
- package/dist/modules/dynamic-credentials.ee/services/resolver-config-expression.service.d.ts +7 -0
- package/dist/modules/dynamic-credentials.ee/services/resolver-config-expression.service.js +46 -0
- package/dist/modules/insights/database/entities/insights-metadata.js +1 -1
- package/dist/modules/insights/database/repositories/insights-by-period-query.helper.js +4 -1
- package/dist/modules/insights/database/repositories/insights-by-period.repository.d.ts +1 -1
- package/dist/modules/insights/insights.module.d.ts +1 -1
- package/dist/modules/insights/insights.service.d.ts +1 -1
- package/dist/modules/log-streaming.ee/database/entities/event-destination.entity.d.ts +6 -0
- package/dist/modules/log-streaming.ee/database/entities/event-destination.entity.js +28 -0
- package/dist/modules/log-streaming.ee/database/entities/index.d.ts +1 -0
- package/dist/modules/log-streaming.ee/database/entities/index.js +5 -0
- package/dist/modules/log-streaming.ee/database/repositories/event-destination.repository.d.ts +5 -0
- package/dist/modules/log-streaming.ee/database/repositories/event-destination.repository.js +25 -0
- package/dist/modules/log-streaming.ee/destinations/message-event-bus-destination-from-db.d.ts +4 -0
- package/dist/modules/log-streaming.ee/destinations/message-event-bus-destination-from-db.js +24 -0
- package/dist/modules/log-streaming.ee/destinations/message-event-bus-destination-sentry.ee.d.ts +17 -0
- package/dist/modules/log-streaming.ee/destinations/message-event-bus-destination-sentry.ee.js +131 -0
- package/dist/modules/log-streaming.ee/destinations/message-event-bus-destination-syslog.ee.d.ts +22 -0
- package/dist/modules/log-streaming.ee/destinations/message-event-bus-destination-syslog.ee.js +116 -0
- package/dist/modules/log-streaming.ee/destinations/message-event-bus-destination-webhook.ee.d.ts +35 -0
- package/dist/modules/log-streaming.ee/destinations/message-event-bus-destination-webhook.ee.js +328 -0
- package/dist/modules/log-streaming.ee/destinations/message-event-bus-destination.ee.d.ts +28 -0
- package/dist/modules/log-streaming.ee/destinations/message-event-bus-destination.ee.js +74 -0
- package/dist/modules/log-streaming.ee/log-streaming-destination.service.d.ts +32 -0
- package/dist/modules/log-streaming.ee/log-streaming-destination.service.js +175 -0
- package/dist/modules/log-streaming.ee/log-streaming.controller.d.ts +15 -0
- package/dist/modules/log-streaming.ee/log-streaming.controller.js +149 -0
- package/dist/modules/log-streaming.ee/log-streaming.module.d.ts +5 -0
- package/dist/modules/log-streaming.ee/log-streaming.module.js +66 -0
- package/dist/modules/mcp/mcp-oauth.helpers.js +0 -0
- package/dist/modules/mcp/mcp.oauth-clients.controller.js +0 -0
- package/dist/modules/mcp/tools/execute-workflow.tool.d.ts +10 -10
- package/dist/modules/mcp/tools/schemas.d.ts +10 -10
- package/dist/modules/source-control.ee/constants.d.ts +17 -0
- package/dist/modules/source-control.ee/constants.js +22 -0
- package/dist/modules/source-control.ee/middleware/source-control-enabled-middleware.ee.d.ts +2 -0
- package/dist/modules/source-control.ee/middleware/source-control-enabled-middleware.ee.js +18 -0
- package/dist/modules/source-control.ee/source-control-export.service.ee.d.ts +37 -0
- package/dist/modules/source-control.ee/source-control-export.service.ee.js +426 -0
- package/dist/modules/source-control.ee/source-control-git.service.ee.d.ts +59 -0
- package/dist/modules/source-control.ee/source-control-git.service.ee.js +427 -0
- package/dist/modules/source-control.ee/source-control-helper.ee.d.ts +49 -0
- package/dist/modules/source-control.ee/source-control-helper.ee.js +241 -0
- package/dist/modules/source-control.ee/source-control-import.service.ee.d.ts +110 -0
- package/dist/modules/source-control.ee/source-control-import.service.ee.js +957 -0
- package/dist/modules/source-control.ee/source-control-preferences.service.ee.d.ts +46 -0
- package/dist/modules/source-control.ee/source-control-preferences.service.ee.js +309 -0
- package/dist/modules/source-control.ee/source-control-resource-helper.d.ts +4 -0
- package/dist/modules/source-control.ee/source-control-resource-helper.js +20 -0
- package/dist/modules/source-control.ee/source-control-scoped.service.d.ts +17 -0
- package/dist/modules/source-control.ee/source-control-scoped.service.js +134 -0
- package/dist/modules/source-control.ee/source-control-status.service.ee.d.ts +90 -0
- package/dist/modules/source-control.ee/source-control-status.service.ee.js +596 -0
- package/dist/modules/source-control.ee/source-control.config.d.ts +3 -0
- package/dist/modules/source-control.ee/source-control.config.js +26 -0
- package/dist/modules/source-control.ee/source-control.controller.ee.d.ts +157 -0
- package/dist/modules/source-control.ee/source-control.controller.ee.js +347 -0
- package/dist/modules/source-control.ee/source-control.module.d.ts +4 -0
- package/dist/modules/source-control.ee/source-control.module.js +59 -0
- package/dist/modules/source-control.ee/source-control.service.ee.d.ts +125 -0
- package/dist/modules/source-control.ee/source-control.service.ee.js +466 -0
- package/dist/modules/source-control.ee/types/export-result.d.ts +10 -0
- package/dist/modules/source-control.ee/types/export-result.js +2 -0
- package/dist/modules/source-control.ee/types/exportable-credential.d.ts +14 -0
- package/dist/modules/source-control.ee/types/exportable-credential.js +2 -0
- package/dist/modules/source-control.ee/types/exportable-folders.d.ts +11 -0
- package/dist/modules/source-control.ee/types/exportable-folders.js +2 -0
- package/dist/modules/source-control.ee/types/exportable-project.d.ts +17 -0
- package/dist/modules/source-control.ee/types/exportable-project.js +2 -0
- package/dist/modules/source-control.ee/types/exportable-tags.d.ts +5 -0
- package/dist/modules/source-control.ee/types/exportable-tags.js +2 -0
- package/dist/modules/source-control.ee/types/exportable-variable.d.ts +7 -0
- package/dist/modules/source-control.ee/types/exportable-variable.js +2 -0
- package/dist/modules/source-control.ee/types/exportable-workflow.d.ts +14 -0
- package/dist/modules/source-control.ee/types/exportable-workflow.js +2 -0
- package/dist/modules/source-control.ee/types/import-result.d.ts +20 -0
- package/dist/modules/source-control.ee/types/import-result.js +2 -0
- package/dist/modules/source-control.ee/types/key-pair-type.d.ts +1 -0
- package/dist/modules/source-control.ee/types/key-pair-type.js +2 -0
- package/dist/modules/source-control.ee/types/key-pair.d.ts +4 -0
- package/dist/modules/source-control.ee/types/key-pair.js +2 -0
- package/dist/modules/source-control.ee/types/requests.d.ts +21 -0
- package/dist/modules/source-control.ee/types/requests.js +2 -0
- package/dist/modules/source-control.ee/types/resource-owner.d.ts +17 -0
- package/dist/modules/source-control.ee/types/resource-owner.js +2 -0
- package/dist/modules/source-control.ee/types/source-control-commit.d.ts +3 -0
- package/dist/modules/source-control.ee/types/source-control-commit.js +20 -0
- package/dist/modules/source-control.ee/types/source-control-context.d.ts +7 -0
- package/dist/modules/source-control.ee/types/source-control-context.js +16 -0
- package/dist/modules/source-control.ee/types/source-control-disconnect.d.ts +3 -0
- package/dist/modules/source-control.ee/types/source-control-disconnect.js +21 -0
- package/dist/modules/source-control.ee/types/source-control-generate-key-pair.d.ts +4 -0
- package/dist/modules/source-control.ee/types/source-control-generate-key-pair.js +21 -0
- package/dist/modules/source-control.ee/types/source-control-get-status.d.ts +10 -0
- package/dist/modules/source-control.ee/types/source-control-get-status.js +42 -0
- package/dist/modules/source-control.ee/types/source-control-preferences.d.ts +17 -0
- package/dist/modules/source-control.ee/types/source-control-preferences.js +88 -0
- package/dist/modules/source-control.ee/types/source-control-push.d.ts +3 -0
- package/dist/modules/source-control.ee/types/source-control-push.js +21 -0
- package/dist/modules/source-control.ee/types/source-control-set-branch.d.ts +3 -0
- package/dist/modules/source-control.ee/types/source-control-set-branch.js +20 -0
- package/dist/modules/source-control.ee/types/source-control-set-read-only.d.ts +3 -0
- package/dist/modules/source-control.ee/types/source-control-set-read-only.js +20 -0
- package/dist/modules/source-control.ee/types/source-control-stage.d.ts +5 -0
- package/dist/modules/source-control.ee/types/source-control-stage.js +31 -0
- package/dist/modules/source-control.ee/types/source-control-workflow-version-id.d.ts +12 -0
- package/dist/modules/source-control.ee/types/source-control-workflow-version-id.js +2 -0
- package/dist/modules/sso-oidc/constants.d.ts +3 -0
- package/dist/modules/sso-oidc/constants.js +6 -0
- package/dist/modules/sso-oidc/oidc.controller.ee.d.ts +23 -0
- package/dist/modules/sso-oidc/oidc.controller.ee.js +123 -0
- package/dist/modules/sso-oidc/oidc.service.ee.d.ts +56 -0
- package/dist/modules/sso-oidc/oidc.service.ee.js +468 -0
- package/dist/modules/sso-oidc/sso-oidc.module.d.ts +4 -0
- package/dist/modules/sso-oidc/sso-oidc.module.js +55 -0
- package/dist/modules/sso-saml/constants.d.ts +3 -0
- package/dist/modules/sso-saml/constants.js +6 -0
- package/dist/modules/sso-saml/errors/invalid-saml-metadata-url.error.d.ts +4 -0
- package/dist/modules/sso-saml/errors/invalid-saml-metadata-url.error.js +10 -0
- package/dist/modules/sso-saml/errors/invalid-saml-metadata.error.d.ts +4 -0
- package/dist/modules/sso-saml/errors/invalid-saml-metadata.error.js +10 -0
- package/dist/modules/sso-saml/middleware/saml-enabled-middleware.d.ts +3 -0
- package/dist/modules/sso-saml/middleware/saml-enabled-middleware.js +22 -0
- package/dist/modules/sso-saml/saml-helpers.d.ts +19 -0
- package/dist/modules/sso-saml/saml-helpers.js +129 -0
- package/dist/modules/sso-saml/saml-validator.d.ts +16 -0
- package/dist/modules/sso-saml/saml-validator.js +129 -0
- package/dist/modules/sso-saml/saml.controller.ee.d.ts +50 -0
- package/dist/modules/sso-saml/saml.controller.ee.js +227 -0
- package/dist/modules/sso-saml/saml.service.ee.d.ts +52 -0
- package/dist/modules/sso-saml/saml.service.ee.js +462 -0
- package/dist/modules/sso-saml/schema/metadata-exchange.xsd.d.ts +2 -0
- package/dist/modules/sso-saml/schema/metadata-exchange.xsd.js +118 -0
- package/dist/modules/sso-saml/schema/oasis-200401-wss-wssecurity-secext-1.0.xsd.d.ts +2 -0
- package/dist/modules/sso-saml/schema/oasis-200401-wss-wssecurity-secext-1.0.xsd.js +201 -0
- package/dist/modules/sso-saml/schema/oasis-200401-wss-wssecurity-utility-1.0.xsd.d.ts +2 -0
- package/dist/modules/sso-saml/schema/oasis-200401-wss-wssecurity-utility-1.0.xsd.js +114 -0
- package/dist/modules/sso-saml/schema/saml-schema-assertion-2.0.xsd.d.ts +2 -0
- package/dist/modules/sso-saml/schema/saml-schema-assertion-2.0.xsd.js +289 -0
- package/dist/modules/sso-saml/schema/saml-schema-metadata-2.0.xsd.d.ts +2 -0
- package/dist/modules/sso-saml/schema/saml-schema-metadata-2.0.xsd.js +344 -0
- package/dist/modules/sso-saml/schema/saml-schema-protocol-2.0.xsd.d.ts +2 -0
- package/dist/modules/sso-saml/schema/saml-schema-protocol-2.0.xsd.js +308 -0
- package/dist/modules/sso-saml/schema/ws-addr.xsd.d.ts +2 -0
- package/dist/modules/sso-saml/schema/ws-addr.xsd.js +143 -0
- package/dist/modules/sso-saml/schema/ws-authorization.xsd.d.ts +2 -0
- package/dist/modules/sso-saml/schema/ws-authorization.xsd.js +151 -0
- package/dist/modules/sso-saml/schema/ws-federation.xsd.d.ts +2 -0
- package/dist/modules/sso-saml/schema/ws-federation.xsd.js +476 -0
- package/dist/modules/sso-saml/schema/ws-securitypolicy-1.2.xsd.d.ts +2 -0
- package/dist/modules/sso-saml/schema/ws-securitypolicy-1.2.xsd.js +1211 -0
- package/dist/modules/sso-saml/schema/xenc-schema.xsd.d.ts +2 -0
- package/dist/modules/sso-saml/schema/xenc-schema.xsd.js +151 -0
- package/dist/modules/sso-saml/schema/xml.xsd.d.ts +2 -0
- package/dist/modules/sso-saml/schema/xml.xsd.js +123 -0
- package/dist/modules/sso-saml/schema/xmldsig-core-schema.xsd.d.ts +2 -0
- package/dist/modules/sso-saml/schema/xmldsig-core-schema.xsd.js +324 -0
- package/dist/modules/sso-saml/service-provider.ee.d.ts +6 -0
- package/dist/modules/sso-saml/service-provider.ee.js +44 -0
- package/dist/modules/sso-saml/sso-saml.module.d.ts +4 -0
- package/dist/modules/sso-saml/sso-saml.module.js +55 -0
- package/dist/modules/sso-saml/types.d.ts +4 -0
- package/dist/modules/sso-saml/types.js +2 -0
- package/dist/modules/sso-saml/views/init-sso-post.d.ts +2 -0
- package/dist/modules/sso-saml/views/init-sso-post.js +16 -0
- package/dist/node-lib.d.ts +10 -0
- package/dist/node-lib.js +137 -0
- package/dist/push/index.d.ts +1 -1
- package/dist/server.d.ts +1 -0
- package/dist/server.js +1 -0
- package/dist/services/cors-service.d.ts +7 -0
- package/dist/services/cors-service.js +50 -0
- package/dist/services/static-auth-service.d.ts +4 -0
- package/dist/services/static-auth-service.js +29 -0
- package/dist/sso.ee/saml/schema/metadata-exchange.xsd.js +0 -0
- package/dist/task-runners/task-broker/errors/task-runner-accept-timeout.error.js +0 -0
- package/dist/typecheck.tsbuildinfo +1 -0
- package/dist/user-management/email/templates/workflow-failure.handlebars +211 -0
- package/dist/utils/cors.util.d.ts +2 -0
- package/dist/utils/cors.util.js +17 -0
- package/dist/webhooks/waiting-forms.js +3 -3
- package/dist/webhooks/waiting-webhooks.js +2 -0
- package/dist/workflows/workflow.service.d.ts +2 -2
- package/dist/workflows/workflows.controller.d.ts +6 -6
- package/package.json +22 -21
|
@@ -0,0 +1,957 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
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;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.SourceControlImportService = void 0;
|
|
16
|
+
const backend_common_1 = require("@n8n/backend-common");
|
|
17
|
+
const db_1 = require("@n8n/db");
|
|
18
|
+
const di_1 = require("@n8n/di");
|
|
19
|
+
const permissions_1 = require("@n8n/permissions");
|
|
20
|
+
const typeorm_1 = require("@n8n/typeorm");
|
|
21
|
+
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
22
|
+
const isEqual_1 = __importDefault(require("lodash/isEqual"));
|
|
23
|
+
const n8n_core_1 = require("n8n-core");
|
|
24
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
25
|
+
const promises_1 = require("node:fs/promises");
|
|
26
|
+
const path_1 = __importDefault(require("path"));
|
|
27
|
+
const uuid_1 = require("uuid");
|
|
28
|
+
const active_workflow_manager_1 = require("../../active-workflow-manager");
|
|
29
|
+
const credentials_service_1 = require("../../credentials/credentials.service");
|
|
30
|
+
const response_helper_1 = require("../../response-helper");
|
|
31
|
+
const tag_service_1 = require("../../services/tag.service");
|
|
32
|
+
const utils_1 = require("../../utils");
|
|
33
|
+
const workflow_history_service_1 = require("../../workflows/workflow-history/workflow-history.service");
|
|
34
|
+
const workflow_service_1 = require("../../workflows/workflow.service");
|
|
35
|
+
const constants_1 = require("./constants");
|
|
36
|
+
const source_control_helper_ee_1 = require("./source-control-helper.ee");
|
|
37
|
+
const source_control_scoped_service_1 = require("./source-control-scoped.service");
|
|
38
|
+
const variables_service_ee_1 = require("../../environments.ee/variables/variables.service.ee");
|
|
39
|
+
const findOwnerProject = (owner, accessibleProjects) => {
|
|
40
|
+
if (typeof owner === 'string') {
|
|
41
|
+
return accessibleProjects.find((project) => project.projectRelations.some((r) => r.role.slug === permissions_1.PROJECT_OWNER_ROLE_SLUG && r.user.email === owner));
|
|
42
|
+
}
|
|
43
|
+
if (owner.type === 'personal') {
|
|
44
|
+
return accessibleProjects.find((project) => project.type === 'personal' &&
|
|
45
|
+
project.projectRelations.some((r) => r.role.slug === permissions_1.PROJECT_OWNER_ROLE_SLUG && r.user.email === owner.personalEmail));
|
|
46
|
+
}
|
|
47
|
+
return accessibleProjects.find((project) => project.type === 'team' && project.id === owner.teamId);
|
|
48
|
+
};
|
|
49
|
+
const getOwnerFromProject = (remoteOwnerProject) => {
|
|
50
|
+
let owner = undefined;
|
|
51
|
+
if (remoteOwnerProject?.type === 'personal') {
|
|
52
|
+
const personalEmail = remoteOwnerProject.projectRelations?.find((r) => r.role.slug === permissions_1.PROJECT_OWNER_ROLE_SLUG)?.user?.email;
|
|
53
|
+
if (personalEmail) {
|
|
54
|
+
owner = {
|
|
55
|
+
type: 'personal',
|
|
56
|
+
projectId: remoteOwnerProject.id,
|
|
57
|
+
projectName: remoteOwnerProject.name,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else if (remoteOwnerProject?.type === 'team') {
|
|
62
|
+
owner = {
|
|
63
|
+
type: 'team',
|
|
64
|
+
projectId: remoteOwnerProject.id,
|
|
65
|
+
projectName: remoteOwnerProject.name,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return owner;
|
|
69
|
+
};
|
|
70
|
+
let SourceControlImportService = class SourceControlImportService {
|
|
71
|
+
constructor(logger, errorReporter, variablesService, activeWorkflowManager, credentialsRepository, projectRepository, projectRelationRepository, tagRepository, sharedWorkflowRepository, sharedCredentialsRepository, userRepository, variablesRepository, workflowRepository, workflowTagMappingRepository, workflowService, credentialsService, tagService, folderRepository, instanceSettings, sourceControlScopedService, workflowPublishHistoryRepository, workflowHistoryService) {
|
|
72
|
+
this.logger = logger;
|
|
73
|
+
this.errorReporter = errorReporter;
|
|
74
|
+
this.variablesService = variablesService;
|
|
75
|
+
this.activeWorkflowManager = activeWorkflowManager;
|
|
76
|
+
this.credentialsRepository = credentialsRepository;
|
|
77
|
+
this.projectRepository = projectRepository;
|
|
78
|
+
this.projectRelationRepository = projectRelationRepository;
|
|
79
|
+
this.tagRepository = tagRepository;
|
|
80
|
+
this.sharedWorkflowRepository = sharedWorkflowRepository;
|
|
81
|
+
this.sharedCredentialsRepository = sharedCredentialsRepository;
|
|
82
|
+
this.userRepository = userRepository;
|
|
83
|
+
this.variablesRepository = variablesRepository;
|
|
84
|
+
this.workflowRepository = workflowRepository;
|
|
85
|
+
this.workflowTagMappingRepository = workflowTagMappingRepository;
|
|
86
|
+
this.workflowService = workflowService;
|
|
87
|
+
this.credentialsService = credentialsService;
|
|
88
|
+
this.tagService = tagService;
|
|
89
|
+
this.folderRepository = folderRepository;
|
|
90
|
+
this.sourceControlScopedService = sourceControlScopedService;
|
|
91
|
+
this.workflowPublishHistoryRepository = workflowPublishHistoryRepository;
|
|
92
|
+
this.workflowHistoryService = workflowHistoryService;
|
|
93
|
+
this.gitFolder = path_1.default.join(instanceSettings.n8nFolder, constants_1.SOURCE_CONTROL_GIT_FOLDER);
|
|
94
|
+
this.workflowExportFolder = path_1.default.join(this.gitFolder, constants_1.SOURCE_CONTROL_WORKFLOW_EXPORT_FOLDER);
|
|
95
|
+
this.credentialExportFolder = path_1.default.join(this.gitFolder, constants_1.SOURCE_CONTROL_CREDENTIAL_EXPORT_FOLDER);
|
|
96
|
+
this.projectExportFolder = path_1.default.join(this.gitFolder, constants_1.SOURCE_CONTROL_PROJECT_EXPORT_FOLDER);
|
|
97
|
+
}
|
|
98
|
+
async getRemoteVersionIdsFromFiles(context) {
|
|
99
|
+
const remoteWorkflowFiles = await (0, fast_glob_1.default)('*.json', {
|
|
100
|
+
cwd: this.workflowExportFolder,
|
|
101
|
+
absolute: true,
|
|
102
|
+
});
|
|
103
|
+
const accessibleProjects = await this.sourceControlScopedService.getAuthorizedProjectsFromContext(context);
|
|
104
|
+
const remoteWorkflowsRead = await Promise.all(remoteWorkflowFiles.map(async (file) => await this.parseWorkflowFromFile(file)));
|
|
105
|
+
const remoteWorkflowFilesParsed = remoteWorkflowsRead
|
|
106
|
+
.filter((remote) => {
|
|
107
|
+
if (!remote?.id) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
return (context.hasAccessToAllProjects() ||
|
|
111
|
+
(remote.owner && findOwnerProject(remote.owner, accessibleProjects)));
|
|
112
|
+
})
|
|
113
|
+
.map((remote) => {
|
|
114
|
+
const project = remote.owner
|
|
115
|
+
? findOwnerProject(remote.owner, accessibleProjects)
|
|
116
|
+
: undefined;
|
|
117
|
+
return {
|
|
118
|
+
id: remote.id,
|
|
119
|
+
versionId: remote.versionId ?? '',
|
|
120
|
+
name: remote.name,
|
|
121
|
+
parentFolderId: remote.parentFolderId,
|
|
122
|
+
remoteId: remote.id,
|
|
123
|
+
filename: (0, source_control_helper_ee_1.getWorkflowExportPath)(remote.id, this.workflowExportFolder),
|
|
124
|
+
owner: project ? getOwnerFromProject(project) : undefined,
|
|
125
|
+
};
|
|
126
|
+
});
|
|
127
|
+
return remoteWorkflowFilesParsed;
|
|
128
|
+
}
|
|
129
|
+
async getAllLocalVersionIdsFromDb() {
|
|
130
|
+
const localWorkflows = await this.workflowRepository.find({
|
|
131
|
+
relations: ['parentFolder'],
|
|
132
|
+
select: {
|
|
133
|
+
id: true,
|
|
134
|
+
versionId: true,
|
|
135
|
+
name: true,
|
|
136
|
+
updatedAt: true,
|
|
137
|
+
parentFolder: {
|
|
138
|
+
id: true,
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
return localWorkflows.map((local) => {
|
|
143
|
+
let updatedAt;
|
|
144
|
+
if (local.updatedAt instanceof Date) {
|
|
145
|
+
updatedAt = local.updatedAt;
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
this.errorReporter.warn('updatedAt is not a Date', {
|
|
149
|
+
extra: {
|
|
150
|
+
type: typeof local.updatedAt,
|
|
151
|
+
value: local.updatedAt,
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
updatedAt = isNaN(Date.parse(local.updatedAt)) ? new Date() : new Date(local.updatedAt);
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
id: local.id,
|
|
158
|
+
versionId: local.versionId,
|
|
159
|
+
name: local.name,
|
|
160
|
+
localId: local.id,
|
|
161
|
+
parentFolderId: local.parentFolder?.id ?? null,
|
|
162
|
+
filename: (0, source_control_helper_ee_1.getWorkflowExportPath)(local.id, this.workflowExportFolder),
|
|
163
|
+
updatedAt: updatedAt.toISOString(),
|
|
164
|
+
};
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
async getLocalVersionIdsFromDb(context) {
|
|
168
|
+
const localWorkflows = await this.workflowRepository.find({
|
|
169
|
+
relations: {
|
|
170
|
+
parentFolder: true,
|
|
171
|
+
shared: {
|
|
172
|
+
project: {
|
|
173
|
+
projectRelations: {
|
|
174
|
+
user: true,
|
|
175
|
+
role: true,
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
select: {
|
|
181
|
+
id: true,
|
|
182
|
+
versionId: true,
|
|
183
|
+
name: true,
|
|
184
|
+
updatedAt: true,
|
|
185
|
+
parentFolder: {
|
|
186
|
+
id: true,
|
|
187
|
+
},
|
|
188
|
+
shared: {
|
|
189
|
+
project: {
|
|
190
|
+
id: true,
|
|
191
|
+
name: true,
|
|
192
|
+
type: true,
|
|
193
|
+
projectRelations: {
|
|
194
|
+
userId: true,
|
|
195
|
+
role: {
|
|
196
|
+
slug: true,
|
|
197
|
+
},
|
|
198
|
+
user: {
|
|
199
|
+
email: true,
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
role: true,
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
where: this.sourceControlScopedService.getWorkflowsInAdminProjectsFromContextFilter(context),
|
|
207
|
+
});
|
|
208
|
+
return localWorkflows.map((local) => {
|
|
209
|
+
let updatedAt;
|
|
210
|
+
if (local.updatedAt instanceof Date) {
|
|
211
|
+
updatedAt = local.updatedAt;
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
this.errorReporter.warn('updatedAt is not a Date', {
|
|
215
|
+
extra: {
|
|
216
|
+
type: typeof local.updatedAt,
|
|
217
|
+
value: local.updatedAt,
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
updatedAt = isNaN(Date.parse(local.updatedAt)) ? new Date() : new Date(local.updatedAt);
|
|
221
|
+
}
|
|
222
|
+
const remoteOwnerProject = local.shared?.find((s) => s.role === 'workflow:owner')?.project;
|
|
223
|
+
return {
|
|
224
|
+
id: local.id,
|
|
225
|
+
versionId: local.versionId,
|
|
226
|
+
name: local.name,
|
|
227
|
+
localId: local.id,
|
|
228
|
+
parentFolderId: local.parentFolder?.id ?? null,
|
|
229
|
+
filename: (0, source_control_helper_ee_1.getWorkflowExportPath)(local.id, this.workflowExportFolder),
|
|
230
|
+
updatedAt: updatedAt.toISOString(),
|
|
231
|
+
owner: remoteOwnerProject ? getOwnerFromProject(remoteOwnerProject) : undefined,
|
|
232
|
+
};
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
async getRemoteCredentialsFromFiles(context) {
|
|
236
|
+
const remoteCredentialFiles = await (0, fast_glob_1.default)('*.json', {
|
|
237
|
+
cwd: this.credentialExportFolder,
|
|
238
|
+
absolute: true,
|
|
239
|
+
});
|
|
240
|
+
const accessibleProjects = await this.sourceControlScopedService.getAuthorizedProjectsFromContext(context);
|
|
241
|
+
const remoteCredentialFilesRead = await Promise.all(remoteCredentialFiles.map(async (file) => {
|
|
242
|
+
this.logger.debug(`Parsing credential file ${file}`);
|
|
243
|
+
const remote = (0, n8n_workflow_1.jsonParse)(await (0, promises_1.readFile)(file, { encoding: 'utf8' }));
|
|
244
|
+
return remote;
|
|
245
|
+
}));
|
|
246
|
+
const remoteCredentialFilesParsed = remoteCredentialFilesRead
|
|
247
|
+
.filter((remote) => {
|
|
248
|
+
if (!remote?.id) {
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
const owner = remote.ownedBy;
|
|
252
|
+
return (!owner || context.hasAccessToAllProjects() || findOwnerProject(owner, accessibleProjects));
|
|
253
|
+
})
|
|
254
|
+
.map((remote) => {
|
|
255
|
+
const project = remote.ownedBy
|
|
256
|
+
? findOwnerProject(remote.ownedBy, accessibleProjects)
|
|
257
|
+
: null;
|
|
258
|
+
return {
|
|
259
|
+
...remote,
|
|
260
|
+
ownedBy: project
|
|
261
|
+
? {
|
|
262
|
+
type: project.type,
|
|
263
|
+
projectId: project.id,
|
|
264
|
+
projectName: project.name,
|
|
265
|
+
}
|
|
266
|
+
: undefined,
|
|
267
|
+
filename: (0, source_control_helper_ee_1.getCredentialExportPath)(remote.id, this.credentialExportFolder),
|
|
268
|
+
};
|
|
269
|
+
});
|
|
270
|
+
return remoteCredentialFilesParsed.filter((e) => e !== undefined);
|
|
271
|
+
}
|
|
272
|
+
async getLocalCredentialsFromDb(context) {
|
|
273
|
+
const localCredentials = await this.credentialsRepository.find({
|
|
274
|
+
relations: {
|
|
275
|
+
shared: {
|
|
276
|
+
project: {
|
|
277
|
+
projectRelations: {
|
|
278
|
+
user: true,
|
|
279
|
+
role: true,
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
select: {
|
|
285
|
+
id: true,
|
|
286
|
+
name: true,
|
|
287
|
+
type: true,
|
|
288
|
+
isGlobal: true,
|
|
289
|
+
shared: {
|
|
290
|
+
project: {
|
|
291
|
+
id: true,
|
|
292
|
+
name: true,
|
|
293
|
+
type: true,
|
|
294
|
+
projectRelations: {
|
|
295
|
+
userId: true,
|
|
296
|
+
role: {
|
|
297
|
+
slug: true,
|
|
298
|
+
},
|
|
299
|
+
user: {
|
|
300
|
+
email: true,
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
role: true,
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
where: this.sourceControlScopedService.getCredentialsInAdminProjectsFromContextFilter(context),
|
|
308
|
+
});
|
|
309
|
+
return localCredentials.map((local) => {
|
|
310
|
+
const remoteOwnerProject = local.shared?.find((s) => s.role === 'credential:owner')?.project;
|
|
311
|
+
return {
|
|
312
|
+
id: local.id,
|
|
313
|
+
name: local.name,
|
|
314
|
+
type: local.type,
|
|
315
|
+
filename: (0, source_control_helper_ee_1.getCredentialExportPath)(local.id, this.credentialExportFolder),
|
|
316
|
+
ownedBy: remoteOwnerProject ? getOwnerFromProject(remoteOwnerProject) : undefined,
|
|
317
|
+
isGlobal: local.isGlobal,
|
|
318
|
+
};
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
async getRemoteVariablesFromFile() {
|
|
322
|
+
const variablesFile = await (0, fast_glob_1.default)(constants_1.SOURCE_CONTROL_VARIABLES_EXPORT_FILE, {
|
|
323
|
+
cwd: this.gitFolder,
|
|
324
|
+
absolute: true,
|
|
325
|
+
});
|
|
326
|
+
if (variablesFile.length > 0) {
|
|
327
|
+
this.logger.debug(`Importing variables from file ${variablesFile[0]}`);
|
|
328
|
+
return (0, n8n_workflow_1.jsonParse)(await (0, promises_1.readFile)(variablesFile[0], { encoding: 'utf8' }), {
|
|
329
|
+
fallbackValue: [],
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
return [];
|
|
333
|
+
}
|
|
334
|
+
async getLocalGlobalVariablesFromDb() {
|
|
335
|
+
return await this.variablesService.getAllCached({ globalOnly: true });
|
|
336
|
+
}
|
|
337
|
+
async getRemoteFoldersAndMappingsFromFile(context) {
|
|
338
|
+
const foldersFile = await (0, fast_glob_1.default)(constants_1.SOURCE_CONTROL_FOLDERS_EXPORT_FILE, {
|
|
339
|
+
cwd: this.gitFolder,
|
|
340
|
+
absolute: true,
|
|
341
|
+
});
|
|
342
|
+
if (foldersFile.length > 0) {
|
|
343
|
+
this.logger.debug(`Importing folders from file ${foldersFile[0]}`);
|
|
344
|
+
const mappedFolders = (0, n8n_workflow_1.jsonParse)(await (0, promises_1.readFile)(foldersFile[0], { encoding: 'utf8' }), {
|
|
345
|
+
fallbackValue: { folders: [] },
|
|
346
|
+
});
|
|
347
|
+
const accessibleProjects = await this.sourceControlScopedService.getAuthorizedProjectsFromContext(context);
|
|
348
|
+
mappedFolders.folders = mappedFolders.folders.filter((folder) => context.hasAccessToAllProjects() ||
|
|
349
|
+
accessibleProjects.some((project) => project.id === folder.homeProjectId));
|
|
350
|
+
return mappedFolders;
|
|
351
|
+
}
|
|
352
|
+
return { folders: [] };
|
|
353
|
+
}
|
|
354
|
+
async getLocalFoldersAndMappingsFromDb(context) {
|
|
355
|
+
const localFolders = await this.folderRepository.find({
|
|
356
|
+
relations: ['parentFolder', 'homeProject'],
|
|
357
|
+
select: {
|
|
358
|
+
id: true,
|
|
359
|
+
name: true,
|
|
360
|
+
createdAt: true,
|
|
361
|
+
updatedAt: true,
|
|
362
|
+
parentFolder: { id: true },
|
|
363
|
+
homeProject: { id: true },
|
|
364
|
+
},
|
|
365
|
+
where: this.sourceControlScopedService.getFoldersInAdminProjectsFromContextFilter(context),
|
|
366
|
+
});
|
|
367
|
+
return {
|
|
368
|
+
folders: localFolders.map((f) => ({
|
|
369
|
+
id: f.id,
|
|
370
|
+
name: f.name,
|
|
371
|
+
parentFolderId: f.parentFolder?.id ?? null,
|
|
372
|
+
homeProjectId: f.homeProject.id,
|
|
373
|
+
createdAt: f.createdAt.toISOString(),
|
|
374
|
+
updatedAt: f.updatedAt.toISOString(),
|
|
375
|
+
})),
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
async getRemoteTagsAndMappingsFromFile(context) {
|
|
379
|
+
const tagsFile = await (0, fast_glob_1.default)(constants_1.SOURCE_CONTROL_TAGS_EXPORT_FILE, {
|
|
380
|
+
cwd: this.gitFolder,
|
|
381
|
+
absolute: true,
|
|
382
|
+
});
|
|
383
|
+
if (tagsFile.length > 0) {
|
|
384
|
+
this.logger.debug(`Importing tags from file ${tagsFile[0]}`);
|
|
385
|
+
const mappedTags = (0, n8n_workflow_1.jsonParse)(await (0, promises_1.readFile)(tagsFile[0], { encoding: 'utf8' }), { fallbackValue: { tags: [], mappings: [] } });
|
|
386
|
+
const accessibleWorkflows = await this.sourceControlScopedService.getWorkflowsInAdminProjectsFromContext(context);
|
|
387
|
+
if (accessibleWorkflows) {
|
|
388
|
+
mappedTags.mappings = mappedTags.mappings.filter((mapping) => accessibleWorkflows.some((workflow) => workflow.id === mapping.workflowId));
|
|
389
|
+
}
|
|
390
|
+
return mappedTags;
|
|
391
|
+
}
|
|
392
|
+
return { tags: [], mappings: [] };
|
|
393
|
+
}
|
|
394
|
+
async getLocalTagsAndMappingsFromDb(context) {
|
|
395
|
+
const localTags = await this.tagRepository.find({
|
|
396
|
+
select: ['id', 'name'],
|
|
397
|
+
});
|
|
398
|
+
const localMappings = await this.workflowTagMappingRepository.find({
|
|
399
|
+
select: ['workflowId', 'tagId'],
|
|
400
|
+
where: this.sourceControlScopedService.getWorkflowTagMappingInAdminProjectsFromContextFilter(context),
|
|
401
|
+
});
|
|
402
|
+
return { tags: localTags, mappings: localMappings };
|
|
403
|
+
}
|
|
404
|
+
async getRemoteProjectsFromFiles(context) {
|
|
405
|
+
const remoteProjectFiles = await (0, fast_glob_1.default)('*.json', {
|
|
406
|
+
cwd: this.projectExportFolder,
|
|
407
|
+
absolute: true,
|
|
408
|
+
});
|
|
409
|
+
const remoteProjects = await Promise.all(remoteProjectFiles.map(async (file) => {
|
|
410
|
+
this.logger.debug(`Parsing project file ${file}`);
|
|
411
|
+
const fileContent = await (0, promises_1.readFile)(file, { encoding: 'utf8' });
|
|
412
|
+
const parsedProject = (0, n8n_workflow_1.jsonParse)(fileContent);
|
|
413
|
+
return {
|
|
414
|
+
...parsedProject,
|
|
415
|
+
filename: (0, source_control_helper_ee_1.getProjectExportPath)(parsedProject.id, this.projectExportFolder),
|
|
416
|
+
};
|
|
417
|
+
}));
|
|
418
|
+
if (context.hasAccessToAllProjects()) {
|
|
419
|
+
return remoteProjects;
|
|
420
|
+
}
|
|
421
|
+
const accessibleProjects = await this.sourceControlScopedService.getAuthorizedProjectsFromContext(context);
|
|
422
|
+
return remoteProjects.filter((remoteProject) => {
|
|
423
|
+
return findOwnerProject(remoteProject.owner, accessibleProjects);
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
async getLocalTeamProjectsFromDb(context) {
|
|
427
|
+
let where = { type: 'team' };
|
|
428
|
+
if (context) {
|
|
429
|
+
where = {
|
|
430
|
+
type: 'team',
|
|
431
|
+
...(this.sourceControlScopedService.getProjectsWithPushScopeByContextFilter(context) ?? {}),
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
const localProjects = await this.projectRepository.find({
|
|
435
|
+
select: ['id', 'name', 'description', 'icon', 'type'],
|
|
436
|
+
relations: ['variables'],
|
|
437
|
+
where,
|
|
438
|
+
});
|
|
439
|
+
return localProjects.map((local) => this.mapProjectEntityToExportableProjectWithFileName(local));
|
|
440
|
+
}
|
|
441
|
+
mapProjectEntityToExportableProjectWithFileName(project) {
|
|
442
|
+
return {
|
|
443
|
+
id: project.id,
|
|
444
|
+
name: project.name,
|
|
445
|
+
description: project.description,
|
|
446
|
+
icon: project.icon,
|
|
447
|
+
filename: (0, source_control_helper_ee_1.getProjectExportPath)(project.id, this.projectExportFolder),
|
|
448
|
+
type: 'team',
|
|
449
|
+
owner: {
|
|
450
|
+
type: 'team',
|
|
451
|
+
teamId: project.id,
|
|
452
|
+
teamName: project.name,
|
|
453
|
+
},
|
|
454
|
+
variableStubs: project.variables.map((variable) => ({
|
|
455
|
+
id: variable.id,
|
|
456
|
+
key: variable.key,
|
|
457
|
+
type: variable.type,
|
|
458
|
+
value: '',
|
|
459
|
+
})),
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
async importWorkflowFromWorkFolder(candidates, userId) {
|
|
463
|
+
const personalProject = await this.projectRepository.getPersonalProjectForUserOrFail(userId);
|
|
464
|
+
const candidateIds = candidates.map((c) => c.id);
|
|
465
|
+
const existingWorkflows = await this.workflowRepository.findByIds(candidateIds, {
|
|
466
|
+
fields: ['id', 'name', 'versionId', 'active', 'activeVersionId'],
|
|
467
|
+
});
|
|
468
|
+
const folders = await this.folderRepository.find({ select: ['id'] });
|
|
469
|
+
const existingFolderIds = folders.map((f) => f.id);
|
|
470
|
+
const allSharedWorkflows = await this.sharedWorkflowRepository.findWithFields(candidateIds, {
|
|
471
|
+
select: ['workflowId', 'role', 'projectId'],
|
|
472
|
+
});
|
|
473
|
+
const importWorkflowsResult = [];
|
|
474
|
+
for (const candidate of candidates) {
|
|
475
|
+
this.logger.debug(`Importing workflow file ${candidate.file}`);
|
|
476
|
+
const importedWorkflow = await this.parseWorkflowFromFile(candidate.file);
|
|
477
|
+
if (!importedWorkflow?.id) {
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
const existingWorkflow = existingWorkflows.find((e) => e.id === importedWorkflow.id);
|
|
481
|
+
if (existingWorkflow) {
|
|
482
|
+
if (importedWorkflow.isArchived) {
|
|
483
|
+
importedWorkflow.active = false;
|
|
484
|
+
importedWorkflow.activeVersionId = null;
|
|
485
|
+
}
|
|
486
|
+
else {
|
|
487
|
+
importedWorkflow.active = !!existingWorkflow.activeVersionId;
|
|
488
|
+
importedWorkflow.activeVersionId = existingWorkflow.activeVersionId;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
else {
|
|
492
|
+
importedWorkflow.active = false;
|
|
493
|
+
importedWorkflow.activeVersionId = null;
|
|
494
|
+
importedWorkflow.versionId = importedWorkflow.versionId ?? (0, uuid_1.v4)();
|
|
495
|
+
}
|
|
496
|
+
const parentFolderId = importedWorkflow.parentFolderId ?? '';
|
|
497
|
+
this.logger.debug(`Updating workflow id ${importedWorkflow.id ?? 'new'}`);
|
|
498
|
+
const upsertResult = await this.workflowRepository.upsert({
|
|
499
|
+
...importedWorkflow,
|
|
500
|
+
parentFolder: existingFolderIds.includes(parentFolderId) ? { id: parentFolderId } : null,
|
|
501
|
+
}, ['id']);
|
|
502
|
+
if (upsertResult?.identifiers?.length !== 1) {
|
|
503
|
+
throw new n8n_workflow_1.UnexpectedError('Failed to upsert workflow', {
|
|
504
|
+
extra: { workflowId: importedWorkflow.id ?? 'new' },
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
await this.saveOrUpdateWorkflowHistory(importedWorkflow, userId);
|
|
508
|
+
const localOwner = allSharedWorkflows.find((w) => w.workflowId === importedWorkflow.id && w.role === 'workflow:owner');
|
|
509
|
+
await this.syncResourceOwnership({
|
|
510
|
+
resourceId: importedWorkflow.id,
|
|
511
|
+
remoteOwner: importedWorkflow.owner,
|
|
512
|
+
localOwner,
|
|
513
|
+
fallbackProject: personalProject,
|
|
514
|
+
repository: this.sharedWorkflowRepository,
|
|
515
|
+
});
|
|
516
|
+
await this.activateImportedWorkflowIfAlreadyActive({ existingWorkflow, importedWorkflow }, userId);
|
|
517
|
+
importWorkflowsResult.push({
|
|
518
|
+
id: importedWorkflow.id ?? 'unknown',
|
|
519
|
+
name: candidate.file,
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
return importWorkflowsResult.filter((e) => e !== undefined);
|
|
523
|
+
}
|
|
524
|
+
async parseWorkflowFromFile(file) {
|
|
525
|
+
this.logger.debug(`Parsing workflow file ${file}`);
|
|
526
|
+
try {
|
|
527
|
+
const fileContent = await (0, promises_1.readFile)(file, { encoding: 'utf8' });
|
|
528
|
+
return (0, n8n_workflow_1.jsonParse)(fileContent);
|
|
529
|
+
}
|
|
530
|
+
catch (error) {
|
|
531
|
+
this.logger.error(`Failed to parse workflow file ${file}`, { error });
|
|
532
|
+
throw new n8n_workflow_1.UnexpectedError(`Failed to parse workflow file ${file}: ${error instanceof Error ? error.message : String(error)}`);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
async activateImportedWorkflowIfAlreadyActive({ existingWorkflow, importedWorkflow, }, userId) {
|
|
536
|
+
if (!existingWorkflow?.activeVersionId)
|
|
537
|
+
return;
|
|
538
|
+
let didAdd = false;
|
|
539
|
+
const newVersionId = importedWorkflow.versionId ?? (0, uuid_1.v4)();
|
|
540
|
+
try {
|
|
541
|
+
this.logger.debug(`Deactivating workflow id ${existingWorkflow.id}`);
|
|
542
|
+
await this.activeWorkflowManager.remove(existingWorkflow.id);
|
|
543
|
+
if (importedWorkflow.activeVersionId && !importedWorkflow.isArchived) {
|
|
544
|
+
this.logger.debug(`Reactivating workflow id ${existingWorkflow.id}`);
|
|
545
|
+
await this.activeWorkflowManager.add(existingWorkflow.id, 'activate');
|
|
546
|
+
didAdd = true;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
catch (e) {
|
|
550
|
+
const error = (0, n8n_workflow_1.ensureError)(e);
|
|
551
|
+
this.logger.error(`Failed to activate workflow ${existingWorkflow.id}`, { error });
|
|
552
|
+
}
|
|
553
|
+
finally {
|
|
554
|
+
await this.workflowRepository.update({ id: existingWorkflow.id }, { versionId: newVersionId });
|
|
555
|
+
await this.workflowPublishHistoryRepository.addRecord({
|
|
556
|
+
workflowId: existingWorkflow.id,
|
|
557
|
+
versionId: didAdd ? newVersionId : existingWorkflow.activeVersionId,
|
|
558
|
+
event: didAdd ? 'activated' : 'deactivated',
|
|
559
|
+
userId,
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
async importCredentialsFromWorkFolder(candidates, userId) {
|
|
564
|
+
const personalProject = await this.projectRepository.getPersonalProjectForUserOrFail(userId);
|
|
565
|
+
const candidateIds = candidates.map((c) => c.id);
|
|
566
|
+
const existingCredentials = await this.credentialsRepository.find({
|
|
567
|
+
where: {
|
|
568
|
+
id: (0, typeorm_1.In)(candidateIds),
|
|
569
|
+
},
|
|
570
|
+
select: ['id', 'name', 'type', 'data'],
|
|
571
|
+
});
|
|
572
|
+
const existingSharedCredentials = await this.sharedCredentialsRepository.find({
|
|
573
|
+
select: ['credentialsId', 'projectId', 'role'],
|
|
574
|
+
where: {
|
|
575
|
+
credentialsId: (0, typeorm_1.In)(candidateIds),
|
|
576
|
+
role: 'credential:owner',
|
|
577
|
+
},
|
|
578
|
+
});
|
|
579
|
+
let importCredentialsResult = [];
|
|
580
|
+
importCredentialsResult = await Promise.all(candidates.map(async (candidate) => {
|
|
581
|
+
this.logger.debug(`Importing credentials file ${candidate.file}`);
|
|
582
|
+
const credential = (0, n8n_workflow_1.jsonParse)(await (0, promises_1.readFile)(candidate.file, { encoding: 'utf8' }));
|
|
583
|
+
const existingCredential = existingCredentials.find((e) => e.id === credential.id && e.type === credential.type);
|
|
584
|
+
const { name, type, data, id, isGlobal = false } = credential;
|
|
585
|
+
const newCredentialObject = new n8n_core_1.Credentials({ id, name }, type);
|
|
586
|
+
if (existingCredential?.data) {
|
|
587
|
+
newCredentialObject.data = existingCredential.data;
|
|
588
|
+
}
|
|
589
|
+
else {
|
|
590
|
+
const { oauthTokenData, ...rest } = data;
|
|
591
|
+
newCredentialObject.setData(rest);
|
|
592
|
+
}
|
|
593
|
+
this.logger.debug(`Updating credential id ${newCredentialObject.id}`);
|
|
594
|
+
await this.credentialsRepository.upsert({ ...newCredentialObject, isGlobal }, ['id']);
|
|
595
|
+
const localOwner = existingSharedCredentials.find((c) => c.credentialsId === credential.id && c.role === 'credential:owner');
|
|
596
|
+
await this.syncResourceOwnership({
|
|
597
|
+
resourceId: credential.id,
|
|
598
|
+
remoteOwner: credential.ownedBy,
|
|
599
|
+
localOwner,
|
|
600
|
+
fallbackProject: personalProject,
|
|
601
|
+
repository: this.sharedCredentialsRepository,
|
|
602
|
+
});
|
|
603
|
+
return {
|
|
604
|
+
id: newCredentialObject.id,
|
|
605
|
+
name: newCredentialObject.name,
|
|
606
|
+
type: newCredentialObject.type,
|
|
607
|
+
};
|
|
608
|
+
}));
|
|
609
|
+
return importCredentialsResult.filter((e) => e !== undefined);
|
|
610
|
+
}
|
|
611
|
+
async importTagsFromWorkFolder(candidate) {
|
|
612
|
+
let mappedTags;
|
|
613
|
+
try {
|
|
614
|
+
this.logger.debug(`Importing tags from file ${candidate.file}`);
|
|
615
|
+
mappedTags = (0, n8n_workflow_1.jsonParse)(await (0, promises_1.readFile)(candidate.file, { encoding: 'utf8' }), { fallbackValue: { tags: [], mappings: [] } });
|
|
616
|
+
}
|
|
617
|
+
catch (e) {
|
|
618
|
+
const error = (0, n8n_workflow_1.ensureError)(e);
|
|
619
|
+
this.logger.error(`Failed to import tags from file ${candidate.file}`, { error });
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
if (mappedTags.mappings.length === 0 && mappedTags.tags.length === 0) {
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
const existingWorkflowIds = new Set((await this.workflowRepository.find({
|
|
626
|
+
select: ['id'],
|
|
627
|
+
})).map((e) => e.id));
|
|
628
|
+
await Promise.all(mappedTags.tags.map(async (tag) => {
|
|
629
|
+
const findByName = await this.tagRepository.findOne({
|
|
630
|
+
where: { name: tag.name },
|
|
631
|
+
select: ['id'],
|
|
632
|
+
});
|
|
633
|
+
if (findByName && findByName.id !== tag.id) {
|
|
634
|
+
throw new n8n_workflow_1.UserError(`A tag with the name <strong>${tag.name}</strong> already exists locally.<br />Please either rename the local tag, or the remote one with the id <strong>${tag.id}</strong> in the tags.json file.`);
|
|
635
|
+
}
|
|
636
|
+
const tagCopy = this.tagRepository.create(tag);
|
|
637
|
+
await this.tagRepository.upsert(tagCopy, {
|
|
638
|
+
skipUpdateIfNoValuesChanged: true,
|
|
639
|
+
conflictPaths: { id: true },
|
|
640
|
+
});
|
|
641
|
+
}));
|
|
642
|
+
await Promise.all(mappedTags.mappings.map(async (mapping) => {
|
|
643
|
+
if (!existingWorkflowIds.has(String(mapping.workflowId)))
|
|
644
|
+
return;
|
|
645
|
+
await this.workflowTagMappingRepository.upsert({ tagId: String(mapping.tagId), workflowId: String(mapping.workflowId) }, {
|
|
646
|
+
skipUpdateIfNoValuesChanged: true,
|
|
647
|
+
conflictPaths: { tagId: true, workflowId: true },
|
|
648
|
+
});
|
|
649
|
+
}));
|
|
650
|
+
return mappedTags;
|
|
651
|
+
}
|
|
652
|
+
async importFoldersFromWorkFolder(user, candidate) {
|
|
653
|
+
let mappedFolders;
|
|
654
|
+
const projects = await this.projectRepository.find();
|
|
655
|
+
const personalProject = await this.projectRepository.getPersonalProjectForUserOrFail(user.id);
|
|
656
|
+
try {
|
|
657
|
+
this.logger.debug(`Importing folders from file ${candidate.file}`);
|
|
658
|
+
mappedFolders = (0, n8n_workflow_1.jsonParse)(await (0, promises_1.readFile)(candidate.file, { encoding: 'utf8' }), {
|
|
659
|
+
fallbackValue: { folders: [] },
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
catch (e) {
|
|
663
|
+
const error = (0, n8n_workflow_1.ensureError)(e);
|
|
664
|
+
this.logger.error(`Failed to import folders from file ${candidate.file}`, { error });
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
if (mappedFolders.folders.length === 0) {
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
await Promise.all(mappedFolders.folders.map(async (folder) => {
|
|
671
|
+
const folderCopy = this.folderRepository.create({
|
|
672
|
+
id: folder.id,
|
|
673
|
+
name: folder.name,
|
|
674
|
+
homeProject: {
|
|
675
|
+
id: projects.find((p) => p.id === folder.homeProjectId)?.id ?? personalProject.id,
|
|
676
|
+
},
|
|
677
|
+
});
|
|
678
|
+
await this.folderRepository.upsert(folderCopy, {
|
|
679
|
+
skipUpdateIfNoValuesChanged: true,
|
|
680
|
+
conflictPaths: { id: true },
|
|
681
|
+
});
|
|
682
|
+
}));
|
|
683
|
+
await Promise.all(mappedFolders.folders.map(async (folder) => {
|
|
684
|
+
await this.folderRepository.update({ id: folder.id }, {
|
|
685
|
+
parentFolder: folder.parentFolderId ? { id: folder.parentFolderId } : null,
|
|
686
|
+
createdAt: folder.createdAt,
|
|
687
|
+
updatedAt: folder.updatedAt,
|
|
688
|
+
});
|
|
689
|
+
}));
|
|
690
|
+
return mappedFolders;
|
|
691
|
+
}
|
|
692
|
+
async importVariables(variables, valueOverrides) {
|
|
693
|
+
const result = { imported: [] };
|
|
694
|
+
const overriddenKeys = Object.keys(valueOverrides ?? {});
|
|
695
|
+
for (const variable of variables) {
|
|
696
|
+
if (!variable.key) {
|
|
697
|
+
continue;
|
|
698
|
+
}
|
|
699
|
+
if (overriddenKeys.includes(variable.key) && valueOverrides) {
|
|
700
|
+
variable.value = valueOverrides[variable.key];
|
|
701
|
+
overriddenKeys.splice(overriddenKeys.indexOf(variable.key), 1);
|
|
702
|
+
}
|
|
703
|
+
try {
|
|
704
|
+
const variableToUpsert = {
|
|
705
|
+
...variable,
|
|
706
|
+
value: variable.value === '' ? undefined : variable.value,
|
|
707
|
+
project: variable.projectId ? { id: variable.projectId } : null,
|
|
708
|
+
};
|
|
709
|
+
await this.variablesRepository.upsert(variableToUpsert, ['id']);
|
|
710
|
+
}
|
|
711
|
+
catch (errorUpsert) {
|
|
712
|
+
if ((0, response_helper_1.isUniqueConstraintError)(errorUpsert)) {
|
|
713
|
+
this.logger.debug(`Variable ${variable.key} already exists, updating instead`);
|
|
714
|
+
try {
|
|
715
|
+
await this.variablesRepository.update({ key: variable.key }, { ...variable });
|
|
716
|
+
}
|
|
717
|
+
catch (errorUpdate) {
|
|
718
|
+
this.logger.debug(`Failed to update variable ${variable.key}, skipping`);
|
|
719
|
+
this.logger.debug(errorUpdate.message);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
finally {
|
|
724
|
+
result.imported.push(variable.key);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
if (overriddenKeys.length > 0 && valueOverrides) {
|
|
728
|
+
for (const key of overriddenKeys) {
|
|
729
|
+
result.imported.push(key);
|
|
730
|
+
const newVariable = this.variablesRepository.create({
|
|
731
|
+
key,
|
|
732
|
+
value: valueOverrides[key],
|
|
733
|
+
});
|
|
734
|
+
await this.variablesRepository.save(newVariable, { transaction: false });
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
await this.variablesService.updateCache();
|
|
738
|
+
return result;
|
|
739
|
+
}
|
|
740
|
+
async importVariablesFromWorkFolder(candidate, valueOverrides) {
|
|
741
|
+
let importedVariables;
|
|
742
|
+
try {
|
|
743
|
+
this.logger.debug(`Importing variables from file ${candidate.file}`);
|
|
744
|
+
importedVariables = (0, n8n_workflow_1.jsonParse)(await (0, promises_1.readFile)(candidate.file, { encoding: 'utf8' }), { fallbackValue: [] });
|
|
745
|
+
}
|
|
746
|
+
catch (e) {
|
|
747
|
+
this.logger.error(`Failed to import tags from file ${candidate.file}`, { error: e });
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
return await this.importVariables(importedVariables, valueOverrides);
|
|
751
|
+
}
|
|
752
|
+
async importTeamProjectsFromWorkFolder(candidates, pullingUserId) {
|
|
753
|
+
const importResults = [];
|
|
754
|
+
const existingProjectVariables = (await this.variablesService.getAllCached()).filter((v) => v.project);
|
|
755
|
+
for (const candidate of candidates) {
|
|
756
|
+
try {
|
|
757
|
+
this.logger.debug(`Importing project file ${candidate.file}`);
|
|
758
|
+
const project = (0, n8n_workflow_1.jsonParse)(await (0, promises_1.readFile)(candidate.file, { encoding: 'utf8' }));
|
|
759
|
+
if (typeof project.owner !== 'object' ||
|
|
760
|
+
project.owner.type !== 'team' ||
|
|
761
|
+
project.owner.teamId !== project.id) {
|
|
762
|
+
this.logger.warn(`Project ${project.id} has inconsistent owner data, skipping`);
|
|
763
|
+
continue;
|
|
764
|
+
}
|
|
765
|
+
await this.projectRepository.upsert({
|
|
766
|
+
id: project.id,
|
|
767
|
+
name: project.name,
|
|
768
|
+
icon: project.icon,
|
|
769
|
+
description: project.description,
|
|
770
|
+
type: 'team',
|
|
771
|
+
}, ['id']);
|
|
772
|
+
const existingProject = await this.projectRepository.findOne({
|
|
773
|
+
where: { id: project.id },
|
|
774
|
+
});
|
|
775
|
+
const hasExistingAdmin = existingProject &&
|
|
776
|
+
(await this.projectRelationRepository.findOne({
|
|
777
|
+
where: { projectId: project.id, role: { slug: permissions_1.PROJECT_ADMIN_ROLE_SLUG } },
|
|
778
|
+
}));
|
|
779
|
+
if (!hasExistingAdmin) {
|
|
780
|
+
await this.projectRelationRepository.save({
|
|
781
|
+
projectId: project.id,
|
|
782
|
+
userId: pullingUserId,
|
|
783
|
+
role: { slug: permissions_1.PROJECT_ADMIN_ROLE_SLUG },
|
|
784
|
+
});
|
|
785
|
+
this.logger.debug(`Assigned user ${pullingUserId} as admin for project ${project.name}`);
|
|
786
|
+
}
|
|
787
|
+
await this.importVariables(project.variableStubs?.map((v) => ({ ...v, projectId: project.id })) ?? []);
|
|
788
|
+
const deletedVariables = existingProjectVariables.filter((v) => v.project.id === project.id && !project.variableStubs?.some((vs) => vs.id === v.id));
|
|
789
|
+
await this.variablesService.deleteByIds(deletedVariables.map((v) => v.id));
|
|
790
|
+
this.logger.info(`Imported team project: ${project.name}`);
|
|
791
|
+
importResults.push({
|
|
792
|
+
id: project.id,
|
|
793
|
+
name: project.name,
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
catch (error) {
|
|
797
|
+
const errorMessage = (0, n8n_workflow_1.ensureError)(error);
|
|
798
|
+
this.logger.error(`Failed to import project from file ${candidate.file}`, {
|
|
799
|
+
error: errorMessage,
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
return importResults;
|
|
804
|
+
}
|
|
805
|
+
async deleteWorkflowsNotInWorkfolder(user, candidates) {
|
|
806
|
+
for (const candidate of candidates) {
|
|
807
|
+
await this.workflowService.delete(user, candidate.id, true);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
async deleteCredentialsNotInWorkfolder(user, candidates) {
|
|
811
|
+
for (const candidate of candidates) {
|
|
812
|
+
await this.credentialsService.delete(user, candidate.id);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
async deleteVariablesNotInWorkfolder(candidates) {
|
|
816
|
+
for (const candidate of candidates) {
|
|
817
|
+
await this.variablesService.delete(candidate.id);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
async deleteTagsNotInWorkfolder(candidates) {
|
|
821
|
+
for (const candidate of candidates) {
|
|
822
|
+
await this.tagService.delete(candidate.id);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
async deleteFoldersNotInWorkfolder(candidates) {
|
|
826
|
+
if (candidates.length === 0) {
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
const candidateIds = candidates.map((c) => c.id);
|
|
830
|
+
await this.folderRepository.delete({
|
|
831
|
+
id: (0, typeorm_1.In)(candidateIds),
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
async deleteTeamProjectsNotInWorkfolder(candidates) {
|
|
835
|
+
if (candidates.length === 0) {
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
const candidateIds = candidates.map((c) => c.id);
|
|
839
|
+
await this.projectRepository.delete({
|
|
840
|
+
id: (0, typeorm_1.In)(candidateIds),
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
async syncResourceOwnership({ resourceId, remoteOwner, localOwner, fallbackProject, repository, }) {
|
|
844
|
+
let targetOwnerProject = await this.findOwnerProjectInLocalDb(remoteOwner ?? undefined);
|
|
845
|
+
if (!targetOwnerProject) {
|
|
846
|
+
const isSharedResource = remoteOwner && typeof remoteOwner !== 'string' && remoteOwner.type === 'team';
|
|
847
|
+
targetOwnerProject = isSharedResource
|
|
848
|
+
? await this.createTeamProject(remoteOwner)
|
|
849
|
+
: fallbackProject;
|
|
850
|
+
}
|
|
851
|
+
const trx = this.workflowRepository.manager;
|
|
852
|
+
const shouldRemoveOldOwner = localOwner && localOwner.projectId !== targetOwnerProject.id;
|
|
853
|
+
if (shouldRemoveOldOwner) {
|
|
854
|
+
await repository.deleteByIds([resourceId], localOwner.projectId, trx);
|
|
855
|
+
}
|
|
856
|
+
await repository.makeOwner([resourceId], targetOwnerProject.id, trx);
|
|
857
|
+
}
|
|
858
|
+
async findOwnerProjectInLocalDb(owner) {
|
|
859
|
+
if (!owner) {
|
|
860
|
+
return null;
|
|
861
|
+
}
|
|
862
|
+
if (typeof owner === 'string' || owner.type === 'personal') {
|
|
863
|
+
const email = typeof owner === 'string' ? owner : owner.personalEmail;
|
|
864
|
+
const user = await this.userRepository.findOne({ where: { email } });
|
|
865
|
+
if (!user) {
|
|
866
|
+
return null;
|
|
867
|
+
}
|
|
868
|
+
return await this.projectRepository.getPersonalProjectForUserOrFail(user.id);
|
|
869
|
+
}
|
|
870
|
+
else if (owner.type === 'team') {
|
|
871
|
+
return await this.projectRepository.findOne({
|
|
872
|
+
where: { id: owner.teamId },
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
(0, utils_1.assertNever)(owner);
|
|
876
|
+
const errorOwner = owner;
|
|
877
|
+
throw new n8n_workflow_1.UnexpectedError(`Unknown resource owner type "${typeof errorOwner !== 'string' ? errorOwner.type : 'UNKNOWN'}" found when finding owner project`);
|
|
878
|
+
}
|
|
879
|
+
async createTeamProject(owner) {
|
|
880
|
+
let teamProject = null;
|
|
881
|
+
try {
|
|
882
|
+
teamProject = await this.projectRepository.save(this.projectRepository.create({
|
|
883
|
+
id: owner.teamId,
|
|
884
|
+
name: owner.teamName,
|
|
885
|
+
type: 'team',
|
|
886
|
+
}));
|
|
887
|
+
}
|
|
888
|
+
catch (error) {
|
|
889
|
+
teamProject = await this.projectRepository.findOne({
|
|
890
|
+
where: { id: owner.teamId },
|
|
891
|
+
});
|
|
892
|
+
if (!teamProject) {
|
|
893
|
+
throw error;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
return teamProject;
|
|
897
|
+
}
|
|
898
|
+
async saveOrUpdateWorkflowHistory(importedWorkflow, userId) {
|
|
899
|
+
if (!importedWorkflow.versionId || !importedWorkflow.nodes || !importedWorkflow.connections) {
|
|
900
|
+
this.logger.debug('Skipping workflow history - missing versionId, nodes, or connections');
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
const user = await this.userRepository.findOne({ where: { id: userId } });
|
|
904
|
+
const authors = user ? `${user.firstName} ${user.lastName}` : 'Unknown';
|
|
905
|
+
try {
|
|
906
|
+
const existingVersion = await this.workflowHistoryService.findVersion(importedWorkflow.id, importedWorkflow.versionId);
|
|
907
|
+
if (existingVersion) {
|
|
908
|
+
const nodesChanged = !(0, isEqual_1.default)(existingVersion.nodes, importedWorkflow.nodes);
|
|
909
|
+
const connectionsChanged = !(0, isEqual_1.default)(existingVersion.connections, importedWorkflow.connections);
|
|
910
|
+
if (nodesChanged || connectionsChanged) {
|
|
911
|
+
this.logger.debug(`Updating workflow history for versionId ${importedWorkflow.versionId}`);
|
|
912
|
+
await this.workflowHistoryService.updateVersion(importedWorkflow.versionId, importedWorkflow.id, {
|
|
913
|
+
nodes: importedWorkflow.nodes,
|
|
914
|
+
connections: importedWorkflow.connections,
|
|
915
|
+
authors,
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
else {
|
|
919
|
+
this.logger.debug(`Workflow history unchanged for versionId ${importedWorkflow.versionId}`);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
else {
|
|
923
|
+
this.logger.debug(`Creating new workflow history for versionId ${importedWorkflow.versionId}`);
|
|
924
|
+
await this.workflowHistoryService.saveVersion(authors, importedWorkflow, importedWorkflow.id);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
catch (error) {
|
|
928
|
+
this.logger.error(`Failed to save/update workflow history for workflow ${importedWorkflow.id}`, { error: (0, n8n_workflow_1.ensureError)(error) });
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
};
|
|
932
|
+
exports.SourceControlImportService = SourceControlImportService;
|
|
933
|
+
exports.SourceControlImportService = SourceControlImportService = __decorate([
|
|
934
|
+
(0, di_1.Service)(),
|
|
935
|
+
__metadata("design:paramtypes", [backend_common_1.Logger,
|
|
936
|
+
n8n_core_1.ErrorReporter,
|
|
937
|
+
variables_service_ee_1.VariablesService,
|
|
938
|
+
active_workflow_manager_1.ActiveWorkflowManager,
|
|
939
|
+
db_1.CredentialsRepository,
|
|
940
|
+
db_1.ProjectRepository,
|
|
941
|
+
db_1.ProjectRelationRepository,
|
|
942
|
+
db_1.TagRepository,
|
|
943
|
+
db_1.SharedWorkflowRepository,
|
|
944
|
+
db_1.SharedCredentialsRepository,
|
|
945
|
+
db_1.UserRepository,
|
|
946
|
+
db_1.VariablesRepository,
|
|
947
|
+
db_1.WorkflowRepository,
|
|
948
|
+
db_1.WorkflowTagMappingRepository,
|
|
949
|
+
workflow_service_1.WorkflowService,
|
|
950
|
+
credentials_service_1.CredentialsService,
|
|
951
|
+
tag_service_1.TagService,
|
|
952
|
+
db_1.FolderRepository,
|
|
953
|
+
n8n_core_1.InstanceSettings,
|
|
954
|
+
source_control_scoped_service_1.SourceControlScopedService,
|
|
955
|
+
db_1.WorkflowPublishHistoryRepository,
|
|
956
|
+
workflow_history_service_1.WorkflowHistoryService])
|
|
957
|
+
], SourceControlImportService);
|