n8n
Version:
n8n Workflow Automation Tool
962 lines • 61 kB
JavaScript
"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,