diff --git a/package-lock.json b/package-lock.json index 72b3f9e3a2da2..fbc83c10eb4c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "n8n", - "version": "0.189.1", + "version": "0.191.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "n8n", - "version": "0.189.1", + "version": "0.191.0", "workspaces": [ "packages/*" ], @@ -41214,6 +41214,35 @@ "node": ">=0.10.0" } }, + "node_modules/posthog-node": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-1.3.0.tgz", + "integrity": "sha512-2+VhqiY/rKIqKIXyvemBFHbeijHE25sP7eKltnqcFqAssUE6+sX6vusN9A4luzToOqHQkUZexiCKxvuGagh7JA==", + "dependencies": { + "axios": "0.24.0", + "axios-retry": "^3.1.9", + "component-type": "^1.2.1", + "join-component": "^1.1.0", + "md5": "^2.3.0", + "ms": "^2.1.3", + "remove-trailing-slash": "^0.1.1", + "uuid": "^8.3.2" + }, + "bin": { + "posthog": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/posthog-node/node_modules/axios": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz", + "integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==", + "dependencies": { + "follow-redirects": "^1.14.4" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -53609,7 +53638,7 @@ }, "packages/cli": { "name": "n8n", - "version": "0.189.1", + "version": "0.191.0", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "@apidevtools/swagger-cli": "4.0.0", @@ -53660,10 +53689,10 @@ "lodash.split": "^4.4.2", "lodash.unset": "^4.5.2", "mysql2": "~2.3.0", - "n8n-core": "~0.129.0", - "n8n-editor-ui": "~0.155.0", - "n8n-nodes-base": "~0.187.0", - "n8n-workflow": "~0.111.0", + "n8n-core": "~0.131.0", + "n8n-editor-ui": "~0.157.0", + "n8n-nodes-base": "~0.189.0", + "n8n-workflow": "~0.113.0", "nodemailer": "^6.7.1", "oauth-1.0a": "^2.2.6", "open": "^7.0.0", @@ -53673,6 +53702,7 @@ "passport-cookie": "^1.0.9", "passport-jwt": "^4.0.0", "pg": "^8.3.0", + "posthog-node": "^1.3.0", "prom-client": "^13.1.0", "psl": "^1.8.0", "shelljs": "^0.8.5", @@ -53733,7 +53763,7 @@ }, "packages/core": { "name": "n8n-core", - "version": "0.129.0", + "version": "0.131.0", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "axios": "^0.21.1", @@ -53745,7 +53775,7 @@ "form-data": "^4.0.0", "lodash.get": "^4.4.2", "mime-types": "^2.1.27", - "n8n-workflow": "~0.111.0", + "n8n-workflow": "~0.113.0", "oauth-1.0a": "^2.2.6", "p-cancelable": "^2.0.0", "qs": "^6.10.1", @@ -53771,7 +53801,7 @@ }, "packages/design-system": { "name": "n8n-design-system", - "version": "0.29.0", + "version": "0.31.0", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "element-ui": "~2.15.7", @@ -53847,14 +53877,14 @@ }, "packages/editor-ui": { "name": "n8n-editor-ui", - "version": "0.155.0", + "version": "0.157.0", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "@fontsource/open-sans": "^4.5.0", "@fortawesome/free-regular-svg-icons": "^6.1.1", "luxon": "^2.3.0", "monaco-editor": "^0.30.1", - "n8n-design-system": "~0.29.0", + "n8n-design-system": "~0.31.0", "timeago.js": "^4.0.2", "v-click-outside": "^3.1.2", "vue-fragment": "1.5.1", @@ -53906,7 +53936,7 @@ "lodash.get": "^4.4.2", "lodash.set": "^4.3.2", "monaco-editor-webpack-plugin": "^5.0.0", - "n8n-workflow": "~0.111.0", + "n8n-workflow": "~0.113.0", "normalize-wheel": "^1.0.1", "prismjs": "^1.17.1", "quill": "^2.0.0-dev.3", @@ -53932,7 +53962,7 @@ }, "packages/node-dev": { "name": "n8n-node-dev", - "version": "0.68.0", + "version": "0.70.0", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "@oclif/command": "^1.5.18", @@ -53942,8 +53972,8 @@ "change-case": "^4.1.1", "copyfiles": "^2.1.1", "inquirer": "^7.0.1", - "n8n-core": "~0.129.0", - "n8n-workflow": "~0.111.0", + "n8n-core": "~0.131.0", + "n8n-workflow": "~0.113.0", "oauth-1.0a": "^2.2.6", "replace-in-file": "^6.0.0", "request": "^2.88.2", @@ -53963,7 +53993,7 @@ }, "packages/nodes-base": { "name": "n8n-nodes-base", - "version": "0.187.0", + "version": "0.189.0", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "@kafkajs/confluent-schema-registry": "1.0.6", @@ -54001,7 +54031,7 @@ "mqtt": "4.2.6", "mssql": "^8.1.2", "mysql2": "~2.3.0", - "n8n-core": "~0.129.0", + "n8n-core": "~0.131.0", "node-html-markdown": "^1.1.3", "node-ssh": "^12.0.0", "nodemailer": "^6.7.1", @@ -54055,7 +54085,7 @@ "eslint-plugin-n8n-nodes-base": "^1.5.4", "gulp": "^4.0.0", "jest": "^27.4.7", - "n8n-workflow": "~0.111.0", + "n8n-workflow": "~0.113.0", "ts-jest": "^27.1.3", "tslint": "^6.1.2", "typescript": "~4.6.0" @@ -54063,7 +54093,7 @@ }, "packages/workflow": { "name": "n8n-workflow", - "version": "0.111.0", + "version": "0.113.0", "license": "SEE LICENSE IN LICENSE.md", "dependencies": { "@n8n_io/riot-tmpl": "^1.0.1", @@ -84006,10 +84036,10 @@ "lodash.split": "^4.4.2", "lodash.unset": "^4.5.2", "mysql2": "~2.3.0", - "n8n-core": "~0.129.0", - "n8n-editor-ui": "~0.155.0", - "n8n-nodes-base": "~0.187.0", - "n8n-workflow": "~0.111.0", + "n8n-core": "~0.131.0", + "n8n-editor-ui": "~0.157.0", + "n8n-nodes-base": "~0.189.0", + "n8n-workflow": "~0.113.0", "nodemailer": "^6.7.1", "nodemon": "^2.0.2", "oauth-1.0a": "^2.2.6", @@ -84020,6 +84050,7 @@ "passport-cookie": "^1.0.9", "passport-jwt": "^4.0.0", "pg": "^8.3.0", + "posthog-node": "^1.3.0", "prom-client": "^13.1.0", "psl": "^1.8.0", "run-script-os": "^1.0.7", @@ -84061,7 +84092,7 @@ "jest": "^27.4.7", "lodash.get": "^4.4.2", "mime-types": "^2.1.27", - "n8n-workflow": "~0.111.0", + "n8n-workflow": "~0.113.0", "oauth-1.0a": "^2.2.6", "p-cancelable": "^2.0.0", "qs": "^6.10.1", @@ -84187,8 +84218,8 @@ "luxon": "^2.3.0", "monaco-editor": "^0.30.1", "monaco-editor-webpack-plugin": "^5.0.0", - "n8n-design-system": "~0.29.0", - "n8n-workflow": "~0.111.0", + "n8n-design-system": "~0.31.0", + "n8n-workflow": "~0.113.0", "normalize-wheel": "^1.0.1", "prismjs": "^1.17.1", "quill": "^2.0.0-dev.3", @@ -84234,8 +84265,8 @@ "change-case": "^4.1.1", "copyfiles": "^2.1.1", "inquirer": "^7.0.1", - "n8n-core": "~0.129.0", - "n8n-workflow": "~0.111.0", + "n8n-core": "~0.131.0", + "n8n-workflow": "~0.113.0", "oauth-1.0a": "^2.2.6", "replace-in-file": "^6.0.0", "request": "^2.88.2", @@ -84312,8 +84343,8 @@ "mqtt": "4.2.6", "mssql": "^8.1.2", "mysql2": "~2.3.0", - "n8n-core": "~0.129.0", - "n8n-workflow": "~0.111.0", + "n8n-core": "~0.131.0", + "n8n-workflow": "~0.113.0", "node-html-markdown": "^1.1.3", "node-ssh": "^12.0.0", "nodemailer": "^6.7.1", @@ -87238,6 +87269,31 @@ "xtend": "^4.0.0" } }, + "posthog-node": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-1.3.0.tgz", + "integrity": "sha512-2+VhqiY/rKIqKIXyvemBFHbeijHE25sP7eKltnqcFqAssUE6+sX6vusN9A4luzToOqHQkUZexiCKxvuGagh7JA==", + "requires": { + "axios": "0.24.0", + "axios-retry": "^3.1.9", + "component-type": "^1.2.1", + "join-component": "^1.1.0", + "md5": "^2.3.0", + "ms": "^2.1.3", + "remove-trailing-slash": "^0.1.1", + "uuid": "^8.3.2" + }, + "dependencies": { + "axios": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz", + "integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==", + "requires": { + "follow-redirects": "^1.14.4" + } + } + } + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", diff --git a/packages/cli/config/schema.ts b/packages/cli/config/schema.ts index 6eb277754cd67..588572845477e 100644 --- a/packages/cli/config/schema.ts +++ b/packages/cli/config/schema.ts @@ -681,6 +681,13 @@ export const schema = { }, }, + externalFrontendHooksUrls: { + doc: 'URLs to external frontend hooks files, ; separated', + format: String, + default: 'https://public-stage.n8n.cloud/posthog-hooks.js', + env: 'EXTERNAL_FRONTEND_HOOKS_URLS', + }, + externalHookFiles: { doc: 'Files containing external hooks. Multiple files can be separated by colon (":")', format: String, @@ -888,6 +895,26 @@ export const schema = { env: 'N8N_DIAGNOSTICS_ENABLED', }, config: { + posthog: { + apiKey: { + doc: 'API key for PostHog', + format: String, + default: 'phc_4URIAm1uYfJO7j8kWSe0J8lc8IqnstRLS7Jx8NcakHo', + env: 'N8N_DIAGNOSTICS_POSTHOG_API_KEY', + }, + apiHost: { + doc: 'API host for PostHog', + format: String, + default: 'https://app.posthog.com', + env: 'N8N_DIAGNOSTICS_POSTHOG_API_HOST', + }, + disableSessionRecording: { + doc: 'Disable posthog session recording', + format: Boolean, + default: true, + env: 'N8N_DIAGNOSTICS_POSTHOG_DISABLE_RECORDING', + }, + }, frontend: { doc: 'Diagnostics config for frontend.', format: String, diff --git a/packages/cli/package.json b/packages/cli/package.json index eb681cd6acd56..025354c1223de 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -157,6 +157,7 @@ "passport-cookie": "^1.0.9", "passport-jwt": "^4.0.0", "pg": "^8.3.0", + "posthog-node": "^1.3.0", "prom-client": "^13.1.0", "psl": "^1.8.0", "shelljs": "^0.8.5", diff --git a/packages/cli/src/Interfaces.ts b/packages/cli/src/Interfaces.ts index 984cc79839b85..3794ada3e2825 100644 --- a/packages/cli/src/Interfaces.ts +++ b/packages/cli/src/Interfaces.ts @@ -516,6 +516,9 @@ export interface IN8nUISettings { missingPackages?: boolean; executionMode: 'regular' | 'queue'; communityNodesEnabled: boolean; + deployment: { + type: string; + }; isNpmAvailable: boolean; } diff --git a/packages/cli/src/InternalHooks.ts b/packages/cli/src/InternalHooks.ts index 7a39e75948b52..e250063051d13 100644 --- a/packages/cli/src/InternalHooks.ts +++ b/packages/cli/src/InternalHooks.ts @@ -106,16 +106,20 @@ export class InternalHooksClass implements IInternalHooksClass { (note) => note.overlapping, ).length; - return this.telemetry.track('User saved workflow', { - user_id: userId, - workflow_id: workflow.id, - node_graph_string: JSON.stringify(nodeGraph), - notes_count_overlapping: overlappingCount, - notes_count_non_overlapping: notesCount - overlappingCount, - version_cli: this.versionCli, - num_tags: workflow.tags?.length ?? 0, - public_api: publicApi, - }); + return this.telemetry.track( + 'User saved workflow', + { + user_id: userId, + workflow_id: workflow.id, + node_graph_string: JSON.stringify(nodeGraph), + notes_count_overlapping: overlappingCount, + notes_count_non_overlapping: notesCount - overlappingCount, + version_cli: this.versionCli, + num_tags: workflow.tags?.length ?? 0, + public_api: publicApi, + }, + { withPostHog: true }, + ); } async onWorkflowPostExecute( @@ -197,14 +201,18 @@ export class InternalHooksClass implements IInternalHooksClass { } if (runData.data.startData?.destinationNode) { + const telemetryPayload = { + ...manualExecEventProperties, + node_type: TelemetryHelpers.getNodeTypeForName( + workflow, + runData.data.startData?.destinationNode, + )?.type, + node_id: nodeGraphResult.nameIndices[runData.data.startData?.destinationNode], + }; + promises.push( - this.telemetry.track('Manual node exec finished', { - ...manualExecEventProperties, - node_type: TelemetryHelpers.getNodeTypeForName( - workflow, - runData.data.startData?.destinationNode, - )?.type, - node_id: nodeGraphResult.nameIndices[runData.data.startData?.destinationNode], + this.telemetry.track('Manual node exec finished', telemetryPayload, { + withPostHog: true, }), ); } else { @@ -219,7 +227,9 @@ export class InternalHooksClass implements IInternalHooksClass { }); promises.push( - this.telemetry.track('Manual workflow exec finished', manualExecEventProperties), + this.telemetry.track('Manual workflow exec finished', manualExecEventProperties, { + withPostHog: true, + }), ); } } diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index 712f3a4e2d36b..cfcdd23cfbbd6 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -166,6 +166,8 @@ import { isUserManagementEnabled, } from './UserManagement/UserManagementHelper'; import { loadPublicApiVersions } from './PublicApi'; +import { SharedWorkflow } from './databases/entities/SharedWorkflow'; +import * as telemetryScripts from './telemetry/scripts'; require('body-parser-xml')(bodyParser); @@ -334,6 +336,9 @@ class App { onboardingCallPromptEnabled: config.getEnv('onboardingCallPrompt.enabled'), executionMode: config.getEnv('executions.mode'), communityNodesEnabled: config.getEnv('nodes.communityPackages.enabled'), + deployment: { + type: config.getEnv('deployment.type'), + }, isNpmAvailable: false, }; } @@ -2611,6 +2616,36 @@ class App { readIndexFile = readIndexFile.replace(/\/%BASE_PATH%\//g, n8nPath); readIndexFile = readIndexFile.replace(/\/favicon.ico/g, `${n8nPath}favicon.ico`); + const hooksUrls = config.getEnv('externalFrontendHooksUrls'); + + let scriptsString = ''; + + if (hooksUrls) { + scriptsString = hooksUrls.split(';').reduce((acc, curr) => { + return `${acc}`; + }, ''); + } + + if (this.frontendSettings.telemetry.enabled) { + const phLoadingScript = telemetryScripts.createPostHogLoadingScript({ + apiKey: config.getEnv('diagnostics.config.posthog.apiKey'), + apiHost: config.getEnv('diagnostics.config.posthog.apiHost'), + autocapture: false, + disableSessionRecording: config.getEnv( + 'diagnostics.config.posthog.disableSessionRecording', + ), + debug: config.getEnv('logs.level') === 'debug', + }); + + scriptsString += phLoadingScript; + } + + const firstLinkedScriptSegment = '`; diff --git a/packages/cli/test/unit/Telemetry.test.ts b/packages/cli/test/unit/Telemetry.test.ts index 040ca916c5ef6..888aec61664b8 100644 --- a/packages/cli/test/unit/Telemetry.test.ts +++ b/packages/cli/test/unit/Telemetry.test.ts @@ -1,7 +1,7 @@ import { Telemetry } from '../../src/telemetry'; import config from '../../config'; -jest.spyOn(Telemetry.prototype as any, 'createTelemetryClient').mockImplementation(() => { +jest.spyOn(Telemetry.prototype as any, 'initRudderStack').mockImplementation(() => { return { flush: () => {}, identify: () => {}, diff --git a/packages/design-system/src/components/N8nUserInfo/UserInfo.vue b/packages/design-system/src/components/N8nUserInfo/UserInfo.vue index e6acb476ea312..a9c2d214c7f9c 100644 --- a/packages/design-system/src/components/N8nUserInfo/UserInfo.vue +++ b/packages/design-system/src/components/N8nUserInfo/UserInfo.vue @@ -8,7 +8,7 @@ {{email}} Pending -
+
{{firstName}} {{lastName}} {{isCurrentUser ? this.t('nds.userInfo.you') : ''}}
@@ -26,9 +26,10 @@ import N8nText from '../N8nText'; import N8nAvatar from '../N8nAvatar'; import N8nBadge from '../N8nBadge'; import Locale from '../../mixins/locale'; +import { externalHooks } from '@/components/mixins/externalHooks'; import mixins from 'vue-typed-mixins'; -export default mixins(Locale).extend({ +export default mixins(Locale, externalHooks).extend({ name: 'n8n-users-info', components: { N8nAvatar, @@ -52,6 +53,9 @@ export default mixins(Locale).extend({ type: Boolean, }, }, + mounted() { + this.$externalHooks().run('userInfo.mounted', { userInfoRef: this.$refs.userInfo }); + } }); diff --git a/packages/design-system/src/main.d.ts b/packages/design-system/src/main.d.ts index 9a58de1c940e6..2116dd4bcec80 100644 --- a/packages/design-system/src/main.d.ts +++ b/packages/design-system/src/main.d.ts @@ -14,3 +14,4 @@ declare module 'n8n-design-system' { } export * from './types'; +export { locale } from './locale'; diff --git a/packages/editor-ui/src/components/CopyInput.vue b/packages/editor-ui/src/components/CopyInput.vue index d7ed3a72a3924..10567a6ff949d 100644 --- a/packages/editor-ui/src/components/CopyInput.vue +++ b/packages/editor-ui/src/components/CopyInput.vue @@ -2,7 +2,7 @@
- {{ value }} + {{ value }}
{{ copyButtonText }}
@@ -62,6 +62,9 @@ export default mixins(copyPaste, showMessage).extend({ }); }, }, + mounted() { + this.$externalHooks().run('copyInput.mounted', { copyInputValueRef: this.$refs.copyInputValue }); + }, }); diff --git a/packages/editor-ui/src/components/CredentialEdit/CredentialEdit.vue b/packages/editor-ui/src/components/CredentialEdit/CredentialEdit.vue index 945890bebce28..b3605017e52a4 100644 --- a/packages/editor-ui/src/components/CredentialEdit/CredentialEdit.vue +++ b/packages/editor-ui/src/components/CredentialEdit/CredentialEdit.vue @@ -702,6 +702,7 @@ export default mixins(showMessage, nodeHelpers).extend({ } this.$telemetry.track('User saved credentials', trackProperties); + this.$externalHooks().run('credentialEdit.saveCredential', trackProperties); } return credential; diff --git a/packages/editor-ui/src/components/CredentialsList.vue b/packages/editor-ui/src/components/CredentialsList.vue index 2fef894871967..1b5a4a66995dc 100644 --- a/packages/editor-ui/src/components/CredentialsList.vue +++ b/packages/editor-ui/src/components/CredentialsList.vue @@ -16,7 +16,7 @@ />
- + @@ -96,8 +96,9 @@ export default mixins( this.$showError(e, this.$locale.baseText('credentialsList.errorLoadingCredentials')); } this.loading = false; - - this.$externalHooks().run('credentialsList.mounted'); + this.$externalHooks().run('credentialsList.mounted', { + tableRef: this.$refs['table'], + }); this.$telemetry.track('User opened Credentials panel', { workflow_id: this.$store.getters.workflowId }); }, destroyed() { diff --git a/packages/editor-ui/src/components/CredentialsSelectModal.vue b/packages/editor-ui/src/components/CredentialsSelectModal.vue index 3f019fbb73bc3..b8e68f59c8ca0 100644 --- a/packages/editor-ui/src/components/CredentialsSelectModal.vue +++ b/packages/editor-ui/src/components/CredentialsSelectModal.vue @@ -51,11 +51,13 @@