UNPKG

n8n

Version:

n8n Workflow Automation Tool

962 lines • 61 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 __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TelemetryEventRelay = void 0; exports.getSemanticVersioning = getSemanticVersioning; const backend_common_1 = require("@n8n/backend-common"); const config_1 = require("@n8n/config"); const db_1 = require("@n8n/db"); const di_1 = require("@n8n/di"); const permissions_1 = require("@n8n/permissions"); const change_case_1 = require("change-case"); const n8n_core_1 = require("n8n-core"); const n8n_workflow_1 = require("n8n-workflow"); const node_os_1 = __importDefault(require("node:os")); const semver_1 = __importDefault(require("semver")); const config_2 = __importDefault(require("../../config")); const constants_1 = require("../../constants"); const dynamic_credentials_proxy_1 = require("../../credentials/dynamic-credentials-proxy"); const event_service_1 = require("../../events/event.service"); const shared_hook_functions_1 = require("../../execution-lifecycle/shared/shared-hook-functions"); const license_1 = require("../../license"); const node_types_1 = require("../../node-types"); const event_relay_1 = require("./event-relay"); const telemetry_1 = require("../../telemetry"); const MAX_NODE_GRAPH_STRING_SIZE = 24 * 1024; function limitNodeGraphStringSize(nodeGraphString) { if (Buffer.byteLength(nodeGraphString, 'utf8') > MAX_NODE_GRAPH_STRING_SIZE) return '{}'; return nodeGraphString; } function getExecutionTelemetryProperties(source, telemetryMetadata) { const executionSource = source ?? 'user'; if (executionSource !== 'instance_ai') return { execution_source: executionSource }; const { mockDataSources } = telemetryMetadata ?? {}; return { execution_source: executionSource, ...(mockDataSources?.length ? { mock_data_sources: mockDataSources.join(',') } : {}), }; } let TelemetryEventRelay = class TelemetryEventRelay extends event_relay_1.EventRelay { constructor(eventService, telemetry, license, licenseState, globalConfig, instanceSettings, binaryDataConfig, workflowRepository, nodeTypes, sharedWorkflowRepository, projectRelationRepository, credentialsRepository, dynamicCredentialsProxy) { super(eventService); this.eventService = eventService; this.telemetry = telemetry; this.license = license; this.licenseState = licenseState; this.globalConfig = globalConfig; this.instanceSettings = instanceSettings; this.binaryDataConfig = binaryDataConfig; this.workflowRepository = workflowRepository; this.nodeTypes = nodeTypes; this.sharedWorkflowRepository = sharedWorkflowRepository; this.projectRelationRepository = projectRelationRepository; this.credentialsRepository = credentialsRepository; this.dynamicCredentialsProxy = dynamicCredentialsProxy; } async init() { if (!this.globalConfig.diagnostics.enabled) return; await this.telemetry.init(); this.setupListeners({ 'team-project-updated': (event) => this.teamProjectUpdated(event), 'team-project-deleted': (event) => this.teamProjectDeleted(event), 'team-project-created': (event) => this.teamProjectCreated(event), 'source-control-settings-updated': (event) => this.sourceControlSettingsUpdated(event), 'source-control-user-started-pull-ui': (event) => this.sourceControlUserStartedPullUi(event), 'source-control-user-finished-pull-ui': (event) => this.sourceControlUserFinishedPullUi(event), 'source-control-user-pulled-api': (event) => this.sourceControlUserPulledApi(event), 'source-control-user-started-push-ui': (event) => this.sourceControlUserStartedPushUi(event), 'source-control-user-finished-push-ui': (event) => this.sourceControlUserFinishedPushUi(event), 'license-renewal-attempted': (event) => this.licenseRenewalAttempted(event), 'license-community-plus-registered': (event) => this.licenseCommunityPlusRegistered(event), 'variable-created': (event) => this.variableCreated(event), 'variable-updated': (event) => this.variableUpdated(event), 'variable-deleted': (event) => this.variableDeleted(event), 'external-secrets-provider-settings-saved': (event) => this.externalSecretsProviderSettingsSaved(event), 'external-secrets-provider-reloaded': (event) => this.externalSecretsProviderReloaded(event), 'external-secrets-connection-created': (event) => this.externalSecretsConnectionCreated(event), 'external-secrets-connection-updated': (event) => this.externalSecretsConnectionUpdated(event), 'external-secrets-connection-deleted': (event) => this.externalSecretsConnectionDeleted(event), 'external-secrets-system-roles-toggled': (event) => this.externalSecretsSystemRolesToggled(event), 'public-api-invoked': (event) => this.publicApiInvoked(event), 'public-api-key-created': (event) => this.publicApiKeyCreated(event), 'public-api-key-deleted': (event) => this.publicApiKeyDeleted(event), 'community-package-installed': (event) => this.communityPackageInstalled(event), 'community-package-updated': (event) => this.communityPackageUpdated(event), 'community-package-deleted': (event) => this.communityPackageDeleted(event), 'credentials-created': (event) => this.credentialsCreated(event), 'credentials-shared': (event) => this.credentialsShared(event), 'credentials-updated': (event) => this.credentialsUpdated(event), 'credentials-deleted': (event) => this.credentialsDeleted(event), 'credentials-user-disconnected': (event) => this.credentialsUserDisconnected(event), 'private-credential-created': (event) => this.privateCredentialCreated(event), 'private-credential-toggled-to-private': (event) => this.privateCredentialToggledToPrivate(event), 'private-credential-toggled-to-static': (event) => this.privateCredentialToggledToStatic(event), 'private-credential-deleted': (event) => this.privateCredentialDeleted(event), 'private-credential-user-connected': (event) => this.privateCredentialUserConnected(event), 'ldap-general-sync-finished': (event) => this.ldapGeneralSyncFinished(event), 'ldap-settings-updated': (event) => this.ldapSettingsUpdated(event), 'ldap-login-sync-failed': (event) => this.ldapLoginSyncFailed(event), 'login-failed-due-to-ldap-disabled': (event) => this.loginFailedDueToLdapDisabled(event), 'sso-user-project-access-updated': (event) => this.ssoUserProjectAccessUpdated(event), 'sso-user-instance-role-updated': (event) => this.ssoUserInstanceRoleUpdated(event), 'workflow-created': (event) => this.workflowCreated(event), 'workflow-archived': (event) => this.workflowArchived(event), 'workflow-unarchived': (event) => this.workflowUnarchived(event), 'workflow-deleted': (event) => this.workflowDeleted(event), 'workflow-sharing-updated': (event) => this.workflowSharingUpdated(event), 'workflow-saved': async (event) => await this.workflowSaved(event), 'workflow-activated': (event) => this.workflowActivated(event), 'workflow-deactivated': (event) => this.workflowDeactivated(event), 'server-started': async () => await this.serverStarted(), 'session-started': (event) => this.sessionStarted(event), 'instance-stopped': () => this.instanceStopped(), 'instance-owner-setup': async (event) => await this.instanceOwnerSetup(event), 'first-production-workflow-succeeded': (event) => this.firstProductionWorkflowSucceeded(event), 'instance-first-production-workflow-failed': (event) => this.instanceFirstProductionWorkflowFailed(event), 'first-workflow-data-loaded': (event) => this.firstWorkflowDataLoaded(event), 'workflow-post-execute': async (event) => await this.workflowPostExecute(event), 'user-changed-role': (event) => this.userChangedRole(event), 'user-retrieved-user': (event) => this.userRetrievedUser(event), 'user-retrieved-all-users': (event) => this.userRetrievedAllUsers(event), 'user-retrieved-execution': (event) => this.userRetrievedExecution(event), 'user-retrieved-all-executions': (event) => this.userRetrievedAllExecutions(event), 'user-retrieved-workflow': (event) => this.userRetrievedWorkflow(event), 'user-retrieved-workflow-version': (event) => this.userRetrievedWorkflowVersion(event), 'user-retrieved-all-workflows': (event) => this.userRetrievedAllWorkflows(event), 'user-updated': (event) => this.userUpdated(event), 'user-deleted': (event) => this.userDeleted(event), 'user-invited': (event) => this.userInvited(event), 'user-signed-up': (event) => this.userSignedUp(event), 'user-submitted-personalization-survey': (event) => this.userSubmittedPersonalizationSurvey(event), 'email-failed': (event) => this.emailFailed(event), 'user-transactional-email-sent': (event) => this.userTransactionalEmailSent(event), 'user-invite-email-click': (event) => this.userInviteEmailClick(event), 'user-password-reset-email-click': (event) => this.userPasswordResetEmailClick(event), 'user-password-reset-request-click': (event) => this.userPasswordResetRequestClick(event), 'history-compacted': (event) => this.historyCompacted(event), 'instance-policies-updated': (event) => this.instancePoliciesUpdated(event), 'execution-data-revealed': (event) => this.executionDataRevealed(event), 'custom-role-created': (event) => this.customRoleCreated(event), 'custom-role-updated': (event) => this.customRoleUpdated(event), 'custom-role-deleted': (event) => this.customRoleDeleted(event), }); } teamProjectUpdated({ userId, role, members, projectId, }) { this.telemetry.track('Project settings updated', { user_id: userId, role, members: members.map(({ userId: user_id, role }) => ({ user_id, role })), project_id: projectId, }); } teamProjectDeleted({ userId, role, projectId, removalType, targetProjectId, }) { this.telemetry.track('User deleted project', { user_id: userId, role, project_id: projectId, removal_type: removalType, target_project_id: targetProjectId, }); } teamProjectCreated({ userId, role, uiContext }) { this.telemetry.track('User created project', { user_id: userId, role, uiContext, }); } sourceControlSettingsUpdated({ branchName, readOnlyInstance, repoType, connected, connectionType, }) { this.telemetry.track('User updated source control settings', { branch_name: branchName, read_only_instance: readOnlyInstance, repo_type: repoType, connected, connection_type: connectionType, }); } sourceControlUserStartedPullUi({ userId, workflowUpdates, workflowConflicts, credConflicts, }) { this.telemetry.track('User started pull via UI', { user_id: userId, workflow_updates: workflowUpdates, workflow_conflicts: workflowConflicts, cred_conflicts: credConflicts, }); } sourceControlUserFinishedPullUi({ userId, workflowUpdates, }) { this.telemetry.track('User finished pull via UI', { user_id: userId, workflow_updates: workflowUpdates, }); } sourceControlUserPulledApi({ workflowUpdates, forced, }) { this.telemetry.track('User pulled via API', { workflow_updates: workflowUpdates, forced, }); } sourceControlUserStartedPushUi({ userId, workflowsEligible, workflowsEligibleWithConflicts, credsEligible, credsEligibleWithConflicts, variablesEligible, }) { this.telemetry.track('User started push via UI', { user_id: userId, workflows_eligible: workflowsEligible, workflows_eligible_with_conflicts: workflowsEligibleWithConflicts, creds_eligible: credsEligible, creds_eligible_with_conflicts: credsEligibleWithConflicts, variables_eligible: variablesEligible, }); } sourceControlUserFinishedPushUi({ userId, workflowsEligible, workflowsPushed, credsPushed, variablesPushed, }) { this.telemetry.track('User finished push via UI', { user_id: userId, workflows_eligible: workflowsEligible, workflows_pushed: workflowsPushed, creds_pushed: credsPushed, variables_pushed: variablesPushed, }); } licenseRenewalAttempted({ success }) { this.telemetry.track('Instance attempted to refresh license', { success, }); } licenseCommunityPlusRegistered({ userId, email, licenseKey, }) { this.telemetry.track('User registered for license community plus', { user_id: userId, email, licenseKey, }); } variableCreated({ user, projectId }) { this.telemetry.track('User created variable', { user_id: user.id, ...(projectId && { project_id: projectId }), }); } variableUpdated({ user, projectId }) { this.telemetry.track('User updated variable', { user_id: user.id, ...(projectId && { project_id: projectId }), }); } variableDeleted({ user, projectId }) { this.telemetry.track('User deleted variable', { user_id: user.id, ...(projectId && { project_id: projectId }), }); } externalSecretsProviderSettingsSaved({ userId, vaultType, isValid, isNew, errorMessage, }) { this.telemetry.track('User updated external secrets settings', { user_id: userId, vault_type: vaultType, is_valid: isValid, is_new: isNew, error_message: errorMessage, }); } externalSecretsProviderReloaded({ vaultType, }) { this.telemetry.track('User reloaded external secrets', { vault_type: vaultType, }); } externalSecretsConnectionCreated({ userId, userRole, vaultType, projects, }) { this.telemetry.track('User created external secrets connection', { user_id: userId, user_role: userRole, vault_type: vaultType, scope: projects.length === 0 ? 'global' : 'project', project_ids: projects.map((project) => project.id), }); } externalSecretsConnectionUpdated({ userId, userRole, vaultType, projects, }) { this.telemetry.track('User updated external secrets connection', { user_id: userId, user_role: userRole, vault_type: vaultType, scope: projects.length === 0 ? 'global' : 'project', project_ids: projects.map((project) => project.id), }); } externalSecretsConnectionDeleted({ userId, userRole, vaultType, projects, }) { this.telemetry.track('User deleted external secrets connection', { user_id: userId, user_role: userRole, vault_type: vaultType, scope: projects.length === 0 ? 'global' : 'project', project_ids: projects.map((project) => project.id), }); } externalSecretsSystemRolesToggled({ userId, enabled, }) { this.telemetry.track('User toggled external secrets system roles', { user_id: userId, enabled, }); } publicApiInvoked({ userId, path, method, apiVersion, userAgent, }) { this.telemetry.trackApiInvocation({ user_id: userId, path, method, api_version: apiVersion, user_agent: userAgent, }); } publicApiKeyCreated(event) { const { user, publicApi } = event; this.telemetry.track('API key created', { user_id: user.id, public_api: publicApi, }); } publicApiKeyDeleted(event) { const { user, publicApi } = event; this.telemetry.track('API key deleted', { user_id: user.id, public_api: publicApi, }); } communityPackageInstalled({ user, inputString, packageName, success, packageVersion, packageNodeNames, packageAuthor, packageAuthorEmail, failureReason, }) { this.telemetry.track('cnr package install finished', { user_id: user.id, input_string: inputString, package_name: packageName, success, package_version: packageVersion, package_node_names: packageNodeNames, package_author: packageAuthor, package_author_email: packageAuthorEmail, failure_reason: failureReason, }); } communityPackageUpdated({ user, packageName, packageVersionCurrent, packageVersionNew, packageNodeNames, packageAuthor, packageAuthorEmail, }) { this.telemetry.track('cnr package updated', { user_id: user.id, package_name: packageName, package_version_current: packageVersionCurrent, package_version_new: packageVersionNew, package_node_names: packageNodeNames, package_author: packageAuthor, package_author_email: packageAuthorEmail, }); } communityPackageDeleted({ user, packageName, packageVersion, packageNodeNames, packageAuthor, packageAuthorEmail, }) { this.telemetry.track('cnr package deleted', { user_id: user.id, package_name: packageName, package_version: packageVersion, package_node_names: packageNodeNames, package_author: packageAuthor, package_author_email: packageAuthorEmail, }); } credentialsCreated({ user, credentialType, credentialId, projectId, projectType, uiContext, isDynamic, usesExternalSecrets, jweEnabled, }) { this.telemetry.track('User created credentials', { user_id: user.id, user_role: user.role?.slug, credential_type: credentialType, credential_id: credentialId, project_id: projectId, project_type: projectType, uiContext, is_dynamic: isDynamic ?? false, uses_external_secrets: usesExternalSecrets ?? false, jwe_enabled: jweEnabled ?? false, }); } credentialsShared({ user, credentialType, credentialId, userIdSharer, userIdsShareesAdded, shareesRemoved, }) { this.telemetry.track('User updated cred sharing', { user_id: user.id, user_role: user.role?.slug, credential_type: credentialType, credential_id: credentialId, user_id_sharer: userIdSharer, user_ids_sharees_added: userIdsShareesAdded, sharees_removed: shareesRemoved, }); } credentialsUpdated({ user, credentialId, credentialType, isDynamic, usesExternalSecrets, jweEnabled, }) { this.telemetry.track('User updated credentials', { user_id: user.id, user_role: user.role?.slug, credential_type: credentialType, credential_id: credentialId, is_dynamic: isDynamic ?? false, uses_external_secrets: usesExternalSecrets ?? false, jwe_enabled: jweEnabled ?? false, }); } credentialsDeleted({ user, credentialId, credentialType, }) { this.telemetry.track('User deleted credentials', { user_id: user.id, user_role: user.role?.slug, credential_type: credentialType, credential_id: credentialId, }); } credentialsUserDisconnected({ user, credentialId, credentialType, }) { this.telemetry.track('User disconnected own credential connection', { user_id: user.id, user_role: user.role?.slug, credential_type: credentialType, credential_id: credentialId, }); } privateCredentialCreated({ user, credentialId, credentialType, projectId, projectType, }) { this.telemetry.track('User created private credential', { user_id: user.id, user_role: user.role?.slug, credential_type: credentialType, credential_id: credentialId, project_id: projectId, project_type: projectType, }); } privateCredentialToggledToPrivate({ user, credentialId, credentialType, }) { this.telemetry.track('User made credential private', { user_id: user.id, user_role: user.role?.slug, credential_type: credentialType, credential_id: credentialId, }); } privateCredentialToggledToStatic({ user, credentialId, credentialType, }) { this.telemetry.track('User made credential static', { user_id: user.id, user_role: user.role?.slug, credential_type: credentialType, credential_id: credentialId, }); } privateCredentialDeleted({ user, credentialId, credentialType, }) { this.telemetry.track('User deleted private credential', { user_id: user.id, user_role: user.role?.slug, credential_type: credentialType, credential_id: credentialId, }); } privateCredentialUserConnected({ user, credentialId, credentialType, }) { this.telemetry.track('User connected to private credential', { user_id: user.id, user_role: user.role?.slug, credential_type: credentialType, credential_id: credentialId, }); } ldapGeneralSyncFinished({ type, succeeded, usersSynced, error, }) { this.telemetry.track('Ldap general sync finished', { type, succeeded, users_synced: usersSynced, error, }); } ldapSettingsUpdated({ userId, loginIdAttribute, firstNameAttribute, lastNameAttribute, emailAttribute, ldapIdAttribute, searchPageSize, searchTimeout, synchronizationEnabled, synchronizationInterval, loginLabel, loginEnabled, }) { this.telemetry.track('User updated Ldap settings', { user_id: userId, loginIdAttribute, firstNameAttribute, lastNameAttribute, emailAttribute, ldapIdAttribute, searchPageSize, searchTimeout, synchronizationEnabled, synchronizationInterval, loginLabel, loginEnabled, }); } ldapLoginSyncFailed({ error }) { this.telemetry.track('Ldap login sync failed', { error }); } loginFailedDueToLdapDisabled({ userId, }) { this.telemetry.track('User login failed since ldap disabled', { user_ud: userId }); } ssoUserProjectAccessUpdated({ projectsRemoved, projectsAdded, userId, }) { this.telemetry.track('Sso user project access update', { user_id: userId, projects_removed: projectsRemoved, projects_added: projectsAdded, }); } ssoUserInstanceRoleUpdated({ userId, role, }) { this.telemetry.track('Sso user instance role update', { user_id: userId, role }); } workflowCreated({ user, workflow, publicApi, projectId, projectType, uiContext, source = 'ui', }) { const { nodeGraph } = n8n_workflow_1.TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes); this.telemetry.track('User created workflow', { user_id: user.id, workflow_id: workflow.id, node_graph_string: limitNodeGraphStringSize(JSON.stringify(nodeGraph)), public_api: publicApi, project_id: projectId, project_type: projectType, meta: JSON.stringify(workflow.meta), uiContext, source, }); } workflowArchived({ user, workflowId, publicApi }) { this.telemetry.track('User archived workflow', { user_id: user.id, workflow_id: workflowId, public_api: publicApi, }); } workflowUnarchived({ user, workflowId, publicApi, }) { this.telemetry.track('User unarchived workflow', { user_id: user.id, workflow_id: workflowId, public_api: publicApi, }); } workflowDeleted({ user, workflowId, publicApi }) { this.telemetry.track('User deleted workflow', { user_id: user.id, workflow_id: workflowId, public_api: publicApi, }); } workflowActivated({ user, workflowId, publicApi, source = 'ui', }) { this.telemetry.track('User activated workflow', { user_id: user.id, workflow_id: workflowId, public_api: publicApi, source, }); } workflowDeactivated({ user, workflowId, publicApi, deactivatedVersionId, source = 'ui', }) { this.telemetry.track('User deactivated workflow', { user_id: user.id, workflow_id: workflowId, public_api: publicApi, deactivated_version_id: deactivatedVersionId, source, }); } workflowSharingUpdated({ workflowId, userIdSharer, userIdList, }) { this.telemetry.track('User updated workflow sharing', { workflow_id: workflowId, user_id_sharer: userIdSharer, user_id_list: userIdList, }); } async workflowSaved({ user, workflow, publicApi, previousWorkflow, aiBuilderAssisted, settingsChanged, source = 'ui', }) { const isCloudDeployment = this.globalConfig.deployment.type === 'cloud'; const { nodeGraph } = n8n_workflow_1.TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes, { isCloudDeployment, }); let userRole = undefined; const role = await this.sharedWorkflowRepository.findSharingRole(user.id, workflow.id); if (role) { userRole = role === 'workflow:owner' ? 'owner' : 'sharee'; } else { const workflowOwner = await this.sharedWorkflowRepository.getWorkflowOwningProject(workflow.id); if (workflowOwner) { const projectRole = await this.projectRelationRepository.findProjectRole({ userId: user.id, projectId: workflowOwner.id, }); if (projectRole && projectRole?.slug !== permissions_1.PROJECT_OWNER_ROLE_SLUG) { userRole = 'member'; } } } const notesCount = Object.keys(nodeGraph.notes).length; const overlappingCount = Object.values(nodeGraph.notes).filter((note) => note.overlapping).length; let workflowEditedNoPos = false; let credentialEdited = false; if (previousWorkflow) { workflowEditedNoPos = (0, n8n_workflow_1.hasNonPositionalChanges)(previousWorkflow.nodes, workflow.nodes, previousWorkflow.connections, workflow.connections); credentialEdited = (0, n8n_workflow_1.hasCredentialChanges)(previousWorkflow.nodes, workflow.nodes); } let credentialResolverId = undefined; if (settingsChanged?.credentialResolverId) { credentialResolverId = settingsChanged.credentialResolverId.to ?? this.dynamicCredentialsProxy.getSystemResolverId() ?? undefined; } let redactionPolicy = undefined; if (settingsChanged?.redactionPolicy) { redactionPolicy = settingsChanged.redactionPolicy.to; } const identityExtractorChanged = this.detectIdentityExtractorChanges(previousWorkflow, workflow); this.telemetry.track('User saved workflow', { user_id: user.id, workflow_id: workflow.id, node_graph_string: limitNodeGraphStringSize(JSON.stringify(nodeGraph)), notes_count_overlapping: overlappingCount, notes_count_non_overlapping: notesCount - overlappingCount, version_cli: constants_1.N8N_VERSION, num_tags: workflow.tags?.length ?? 0, public_api: publicApi, sharing_role: userRole, meta: JSON.stringify(workflow.meta), workflow_edited_no_pos: workflowEditedNoPos, credential_edited: credentialEdited, ai_builder_assisted: aiBuilderAssisted ?? false, credential_resolver_id: credentialResolverId, identity_extractor_changed: identityExtractorChanged, redaction_policy: redactionPolicy, source, }); } async workflowPostExecute({ workflow, runData, userId, source, telemetryMetadata, }) { if (!workflow.id) { return; } const executionTelemetryProperties = getExecutionTelemetryProperties(source, telemetryMetadata); const telemetryProperties = { workflow_id: workflow.id, is_manual: false, version_cli: constants_1.N8N_VERSION, success: false, ...executionTelemetryProperties, used_dynamic_credentials: Object.values(runData?.data?.resultData?.runData ?? {}).some((taskDataList) => taskDataList.some((taskData) => taskData.usedDynamicCredentials)), }; if (userId) { telemetryProperties.user_id = userId; } if (runData?.data.resultData.error?.message?.includes('canceled')) { runData.status = 'canceled'; } telemetryProperties.success = !!runData?.finished; const executionStatus = runData ? (0, shared_hook_functions_1.determineFinalExecutionStatus)(runData) : 'unknown'; if (runData !== undefined) { telemetryProperties.execution_mode = runData.mode; telemetryProperties.is_manual = runData.mode === 'manual'; telemetryProperties.crashed = executionStatus === 'crashed'; let nodeGraphResult = null; if (!telemetryProperties.success && runData?.data.resultData.error) { if (n8n_workflow_1.TelemetryHelpers.userInInstanceRanOutOfFreeAiCredits(runData)) { this.telemetry.track('User ran out of free AI credits'); } telemetryProperties.error_message = runData?.data.resultData.error.message; let errorNodeName = 'node' in runData?.data.resultData.error ? runData?.data.resultData.error.node?.name : undefined; telemetryProperties.error_node_type = 'node' in runData?.data.resultData.error ? runData?.data.resultData.error.node?.type : undefined; if (runData.data.resultData.lastNodeExecuted) { const lastNode = n8n_workflow_1.TelemetryHelpers.getNodeTypeForName(workflow, runData.data.resultData.lastNodeExecuted); if (lastNode !== undefined) { telemetryProperties.error_node_type = lastNode.type; errorNodeName = lastNode.name; } } if (telemetryProperties.is_manual) { nodeGraphResult = n8n_workflow_1.TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes, { runData: runData.data.resultData?.runData, }); telemetryProperties.node_graph = nodeGraphResult.nodeGraph; telemetryProperties.node_graph_string = limitNodeGraphStringSize(JSON.stringify(nodeGraphResult.nodeGraph)); if (errorNodeName) { telemetryProperties.error_node_id = nodeGraphResult.nameIndices[errorNodeName]; } } } if (telemetryProperties.is_manual) { if (!nodeGraphResult) { nodeGraphResult = n8n_workflow_1.TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes, { runData: runData.data.resultData?.runData, }); } let userRole = undefined; if (userId) { const role = await this.sharedWorkflowRepository.findSharingRole(userId, workflow.id); if (role) { userRole = role === 'workflow:owner' ? 'owner' : 'sharee'; } } const manualExecEventProperties = { user_id: userId, workflow_id: workflow.id, status: executionStatus, executionStatus: runData?.status ?? 'unknown', error_message: telemetryProperties.error_message, error_node_type: telemetryProperties.error_node_type, node_graph_string: telemetryProperties.node_graph_string, error_node_id: telemetryProperties.error_node_id, webhook_domain: null, sharing_role: userRole, credential_type: null, is_managed: false, eval_rows_left: null, meta: JSON.stringify(workflow.meta), used_dynamic_credentials: telemetryProperties.used_dynamic_credentials, ...executionTelemetryProperties, ...n8n_workflow_1.TelemetryHelpers.resolveAIMetrics(workflow.nodes, this.nodeTypes), ...n8n_workflow_1.TelemetryHelpers.resolveVectorStoreMetrics(workflow.nodes, this.nodeTypes, runData), ...n8n_workflow_1.TelemetryHelpers.extractLastExecutedNodeStructuredOutputErrorInfo(workflow, this.nodeTypes, runData), }; if (!manualExecEventProperties.node_graph_string) { nodeGraphResult = n8n_workflow_1.TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes, { runData: runData.data.resultData?.runData, }); manualExecEventProperties.node_graph_string = limitNodeGraphStringSize(JSON.stringify(nodeGraphResult.nodeGraph)); } nodeGraphResult?.evaluationTriggerNodeNames?.forEach((name) => { const rowsLeft = runData.data.resultData.runData[name]?.[0]?.data?.main?.[0]?.[0]?.json?._rowsLeft; if (typeof rowsLeft === 'number') { manualExecEventProperties.eval_rows_left = rowsLeft; } }); if (runData.data.startData?.destinationNode) { const credentialsData = n8n_workflow_1.TelemetryHelpers.extractLastExecutedNodeCredentialData(runData); if (credentialsData) { manualExecEventProperties.credential_type = credentialsData.credentialType; const credential = await this.credentialsRepository.findOneBy({ id: credentialsData.credentialId, }); if (credential) { manualExecEventProperties.is_managed = credential.isManaged; } } const destinationNodeName = runData.data.startData?.destinationNode.nodeName; const telemetryPayload = { ...manualExecEventProperties, node_type: n8n_workflow_1.TelemetryHelpers.getNodeTypeForName(workflow, destinationNodeName)?.type, node_id: nodeGraphResult.nameIndices[destinationNodeName], node_role: n8n_workflow_1.TelemetryHelpers.getNodeRole(destinationNodeName, workflow.connections, this.nodeTypes, workflow.nodes), }; this.telemetry.track('Manual node exec finished', telemetryPayload); } else { for (const name of nodeGraphResult.webhookNodeNames) { const execJson = runData.data.resultData.runData[name]?.[0]?.data?.main?.[0]?.[0] ?.json; if (execJson?.headers?.origin && execJson.headers.origin !== '') { const { get: pslGet } = await Promise.resolve().then(() => __importStar(require('psl'))); manualExecEventProperties.webhook_domain = pslGet(execJson.headers.origin.replace(/^https?:\/\//, '')); } } this.telemetry.track('Manual workflow exec finished', manualExecEventProperties); } } } this.telemetry.trackWorkflowExecution(telemetryProperties); } async serverStarted() { const cpus = node_os_1.default.cpus(); const isS3Selected = this.binaryDataConfig.mode === 's3'; const isS3Available = this.binaryDataConfig.availableModes.includes('s3'); const isS3Licensed = this.license.isBinaryDataS3Licensed(); const authenticationMethod = config_2.default.getEnv('userManagement.authenticationMethod'); const info = { version_cli: constants_1.N8N_VERSION, db_type: this.globalConfig.database.type, n8n_version_notifications_enabled: this.globalConfig.versionNotifications.enabled, n8n_disable_production_main_process: this.globalConfig.endpoints.disableProductionWebhooksOnMainProcess, system_info: { os: { type: node_os_1.default.type(), version: node_os_1.default.version(), }, memory: node_os_1.default.totalmem() / 1024, cpus: { count: cpus.length, model: cpus[0].model, speed: cpus[0].speed, }, is_docker: this.instanceSettings.isDocker, }, execution_variables: { executions_mode: this.globalConfig.executions.mode, executions_timeout: this.globalConfig.executions.timeout, executions_timeout_max: this.globalConfig.executions.maxTimeout, executions_data_save_on_error: this.globalConfig.executions.saveDataOnError, executions_data_save_on_success: this.globalConfig.executions.saveDataOnSuccess, executions_data_save_on_progress: this.globalConfig.executions.saveExecutionProgress, executions_data_save_manual_executions: this.globalConfig.executions.saveDataManualExecutions, executions_data_prune: this.globalConfig.executions.pruneData, executions_data_max_age: this.globalConfig.executions.pruneDataMaxAge, }, workflow_history: { compaction_optimizing_time_window_hours: this.globalConfig.workflowHistoryCompaction.optimizingTimeWindowHours, compaction_trim_on_start_up: this.globalConfig.workflowHistoryCompaction.trimOnStartUp, compaction_trimming_time_window_days: this.globalConfig.workflowHistoryCompaction.trimmingTimeWindowDays, }, n8n_deployment_type: this.globalConfig.deployment.type, n8n_binary_data_mode: this.binaryDataConfig.mode, smtp_set_up: this.globalConfig.userManagement.emails.mode === 'smtp', ldap_allowed: authenticationMethod === 'ldap', saml_enabled: authenticationMethod === 'saml', license_plan_name: this.license.getPlanName(), license_tenant_id: this.globalConfig.license.tenantId, binary_data_s3: isS3Available && isS3Selected && isS3Licensed, multi_main_setup_enabled: this.globalConfig.multiMainSetup.enabled, metrics: { metrics_enabled: this.globalConfig.endpoints.metrics.enable, metrics_category_default: this.globalConfig.endpoints.metrics.includeDefaultMetrics, metrics_category_routes: this.globalConfig.endpoints.metrics.includeApiEndpoints, metrics_category_cache: this.globalConfig.endpoints.metrics.includeCacheMetrics, metrics_category_logs: this.globalConfig.endpoints.metrics.includeMessageEventBusMetrics, metrics_category_queue: this.globalConfig.endpoints.metrics.includeQueueMetrics, }, }; const versionParts = getSemanticVersioning(constants_1.N8N_VERSION); const telemetryInstanceInfo = { n8n_host: this.globalConfig.host, version_cli: constants_1.N8N_VERSION, version_cli_major: versionParts.major, version_cli_minor: versionParts.minor, version_cli_patch: versionParts.patch, release_channel: this.globalConfig.generic.releaseChannel, executions_mode: this.globalConfig.executions.mode, n8n_deployment_type: this.globalConfig.deployment.type, db_type: this.globalConfig.database.type, timezone: this.globalConfig.generic.timezone, default_locale: this.globalConfig.defaultLocale, personalization_enabled: this.globalConfig.personalization.enabled, multi_main_setup_enabled: this.globalConfig.multiMainSetup.enabled, task_runners_mode: this.globalConfig.taskRunners.mode, templates_enabled: this.globalConfig.templates.enabled, ai_enabled: this.globalConfig.ai.enabled, license: { plan_name: this.license.getPlanName(), tenant_id: this.globalConfig.license.tenantId, auto_renewal_enabled: this.globalConfig.license.autoRenewalEnabled, has_activation_key: !!this.globalConfig.license.activationKey, expiry_date: this.license.getExpiryDate()?.toISOString(), termination_date: this.license.getTerminationDate()?.toISOString(), expiring_in_days: this.license.getExpiringInDays(), terminating_in_days: this.license.getTerminatingInDays(), features: this.getLicenseFeatures(), }, smtp_set_up: this.globalConfig.userManagement.emails.mode === 'smtp', ldap_allowed: authenticationMethod === 'ldap', saml_enabled: authenticationMethod === 'saml', }; const firstWorkflow = await this.workflowRepository.findOne({ select: ['createdAt'], order: { createdAt: 'ASC' }, where: {}, }); this.telemetry.groupIdentify({ traits: this.telemetry.sanitizeTelemetryProperties(telemetryInstanceInfo), }); this.telemetry.identify(info); this.telemetry.track('Instance started', { ...info, earliest_workflow_created: firstWorkflow?.createdAt, }); } getLicenseFeatures() { return { customRoles: this.licenseState.isCustomRolesLicensed(), dynamicCredentials: this.licenseState.isDynamicCredentialsLicensed(), personalSpacePolicy: this.licenseState.isPersonalSpacePolicyLicensed(), sharing: this.licenseState.isSharingLicensed(), logStreaming: this.licenseState.isLogStreamingLicensed(), ldap: this.licenseState.isLdapLicensed(), saml: this.licenseState.isSamlLicensed(), oidc: this.licenseState.isOidcLicensed(), mfaEnforcement: this.licenseState.isMFAEnforcementLicensed(), apiKeyScopes: this.licenseState.isApiKeyScopesLicensed(), aiAssistant: this.licenseState.isAiAssistantLicensed(), askAi: this.licenseState.isAskAiLicensed(), aiCredits: this.licenseState.isAiCreditsLicensed(), advancedExecutionFilters: this.licenseState.isAdvancedExecutionFiltersLicensed(), advancedPermissions: this.licenseState.isAdvancedPermissionsLicensed(), debugInEditor: this.licenseState.isDebugInEditorLicensed(), binaryDataS3: this.licenseState.isBinaryDataS3Licensed(), multiMain: this.licenseState.isMultiMainLicensed(), variables: this.licenseState.isVariablesLicensed(), sourceControl: this.licenseState.isSourceControlLicensed(), externalSecrets: this.licenseState.isExternalSecretsLicensed(), apiDisabled: this.licenseState.isAPIDisabled(), workerView: this.licenseState.isWorkerViewLicensed(), projectRoleAdmin: this.licenseState.isProjectRoleAdminLicensed(), projectRoleEditor: this.licenseState.isProjectRoleEditorLicensed(), projectRoleViewer: this.licenseState.isProjectRoleViewerLicensed(), customNpmRegistry: this.licenseState.isCustomNpmRegistryLicensed(), folders: this.licenseState.isFoldersLicensed(), insightsSummary: this.licenseState.isInsightsSummaryLicensed(), insightsDashboard: this.licenseState.isInsightsDashboardLicensed(), insightsHourlyData: this.licenseState.isInsightsHourlyDataLicensed(), workflowDiffs: this.licenseState.isWorkflowDiffsLicensed(), provisioning: this.licenseState.isProvisioningLicensed(), maxUsers: this.licenseState.getMaxUsers(), maxActiveWorkflows: this.licenseState.getMaxActiveWorkflows(), maxVariables: this.licenseState.getMaxVariables(), maxAiCredits: this.licenseState.getMaxAiCredits(), workflowHistoryPruneQuota: this.licenseState.getWorkflowHistoryPruneQuota(), insightsMaxHistory: this.licenseState.getInsightsMaxHistory(), insightsRetentionMaxAge: this.licenseState.getInsightsRetentionMaxAge(), insightsRetentionPruneInterval: this.licenseState.getInsightsRetentionPruneInterval(), maxTeamProjects: this.licenseState.getMaxTeamProjects(), maxWorkflowsWithEvaluations: this.licenseState.getMaxWorkflowsWithEvaluations(), }; } sessionStarted({ pushRef }) { this.telemetry.track('Session started', { session_id: pushRef }); } instanceStopped() { this.telemetry.track('User instance stopped'); } async instanceOwnerSetup({ userId }) { this.telemetry.groupIdentify({ userId, }); this.telemetry.track('Owner finished instance setup', { user_id: userId }); } firstProductionWorkflowSucceeded({ projectId, workflowId, userId, }) { this.telemetry.track('Workflow first prod success', { project_id: projectId, workflow_id: workflowId, user_id: userId ?? undefined, }); } instanceFirstProductionWorkflowFailed({ projectId,