From dd12e016a67bb3c2aa9b4fb8c27f19055b2ac1c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Mon, 1 Jul 2024 10:11:31 +0200 Subject: [PATCH 01/12] feat: Introduce debug info button --- packages/cli/src/services/frontend.service.ts | 31 ++++ packages/editor-ui/src/Interface.ts | 7 +- packages/editor-ui/src/__tests__/defaults.ts | 8 + .../editor-ui/src/components/MainSidebar.vue | 18 +++ .../editor-ui/src/composables/useDebugInfo.ts | 140 ++++++++++++++++++ .../src/plugins/i18n/locales/en.json | 3 + .../editor-ui/src/stores/settings.store.ts | 36 ++++- packages/workflow/src/Interfaces.ts | 15 +- 8 files changed, 252 insertions(+), 6 deletions(-) create mode 100644 packages/editor-ui/src/composables/useDebugInfo.ts diff --git a/packages/cli/src/services/frontend.service.ts b/packages/cli/src/services/frontend.service.ts index eb5e3648465e1..8c4ef1e1aae2c 100644 --- a/packages/cli/src/services/frontend.service.ts +++ b/packages/cli/src/services/frontend.service.ts @@ -1,3 +1,4 @@ +import fs from 'node:fs'; import { Container, Service } from 'typedi'; import uniq from 'lodash/uniq'; import { createWriteStream } from 'fs'; @@ -83,6 +84,8 @@ export class FrontendService { } this.settings = { + isDocker: this.isDocker(), + databaseType: config.getEnv('database.type'), previewMode: process.env.N8N_PREVIEW_MODE === 'true', endpointForm: config.getEnv('endpoints.form'), endpointFormTest: config.getEnv('endpoints.formTest'), @@ -92,6 +95,7 @@ export class FrontendService { saveDataErrorExecution: config.getEnv('executions.saveDataOnError'), saveDataSuccessExecution: config.getEnv('executions.saveDataOnSuccess'), saveManualExecutions: config.getEnv('executions.saveDataManualExecutions'), + saveExecutionProgress: config.getEnv('executions.saveExecutionProgress'), executionTimeout: config.getEnv('executions.timeout'), maxExecutionTimeout: config.getEnv('executions.maxTimeout'), workflowCallerPolicyDefaultOption: config.getEnv('workflows.callerPolicyDefaultOption'), @@ -218,6 +222,15 @@ export class FrontendService { pruneTime: -1, licensePruneTime: -1, }, + pruning: { + isEnabled: config.getEnv('executions.pruneData'), + maxAge: config.getEnv('executions.pruneDataMaxAge'), + maxCount: config.getEnv('executions.pruneDataMaxCount'), + }, + security: { + protocol: config.getEnv('protocol'), + blockFileAccessToN8nFiles: config.getEnv('security.blockFileAccessToN8nFiles'), + }, }; } @@ -269,6 +282,8 @@ export class FrontendService { const isS3Available = config.getEnv('binaryDataManager.availableModes').includes('s3'); const isS3Licensed = this.license.isBinaryDataS3Licensed(); + this.settings.license.planName = this.license.getPlanName(); + // refresh enterprise status Object.assign(this.settings.enterprise, { sharing: this.license.isSharingEnabled(), @@ -368,4 +383,20 @@ export class FrontendService { } } } + + /** + * Whether this instance is running inside a Docker container. + * + * Based on: https://github.com/sindresorhus/is-docker + */ + private isDocker() { + try { + return ( + fs.existsSync('/.dockerenv') || + fs.readFileSync('/proc/self/cgroup', 'utf8').includes('docker') + ); + } catch { + return false; + } + } } diff --git a/packages/editor-ui/src/Interface.ts b/packages/editor-ui/src/Interface.ts index 8b8565c9f660e..c58ecd7c4b32f 100644 --- a/packages/editor-ui/src/Interface.ts +++ b/packages/editor-ui/src/Interface.ts @@ -1123,7 +1123,7 @@ export interface RootState { urlBaseEditor: string; instanceId: string; isNpmAvailable: boolean; - binaryDataMode: string; + binaryDataMode: 'default' | 'filesystem' | 's3'; } export interface NodeMetadataMap { @@ -1380,9 +1380,10 @@ export interface ISettingsState { enabled: boolean; }; onboardingCallPromptEnabled: boolean; - saveDataErrorExecution: string; - saveDataSuccessExecution: string; + saveDataErrorExecution: WorkflowSettings.SaveDataExecution; + saveDataSuccessExecution: WorkflowSettings.SaveDataExecution; saveManualExecutions: boolean; + saveDataProgressExecution: boolean; } export type NodeTypesByTypeNameAndVersion = { diff --git a/packages/editor-ui/src/__tests__/defaults.ts b/packages/editor-ui/src/__tests__/defaults.ts index b1a0e0dec8057..249a9538fd8bc 100644 --- a/packages/editor-ui/src/__tests__/defaults.ts +++ b/packages/editor-ui/src/__tests__/defaults.ts @@ -1,6 +1,13 @@ import type { IN8nUISettings } from 'n8n-workflow'; export const defaultSettings: IN8nUISettings = { + databaseType: 'sqlite', + isDocker: false, + pruning: { + isEnabled: false, + maxAge: 0, + maxCount: 0, + }, allowedModules: {}, communityNodesEnabled: false, defaultLocale: '', @@ -60,6 +67,7 @@ export const defaultSettings: IN8nUISettings = { saveDataErrorExecution: 'DEFAULT', saveDataSuccessExecution: 'DEFAULT', saveManualExecutions: false, + saveExecutionProgress: false, sso: { ldap: { loginEnabled: false, loginLabel: '' }, saml: { loginEnabled: false, loginLabel: '' }, diff --git a/packages/editor-ui/src/components/MainSidebar.vue b/packages/editor-ui/src/components/MainSidebar.vue index c66e0a89b5c68..5bbd6440bc7fb 100644 --- a/packages/editor-ui/src/components/MainSidebar.vue +++ b/packages/editor-ui/src/components/MainSidebar.vue @@ -128,6 +128,9 @@ import { useDebounce } from '@/composables/useDebounce'; import { useBecomeTemplateCreatorStore } from '@/components/BecomeTemplateCreatorCta/becomeTemplateCreatorStore'; import ProjectNavigation from '@/components/Projects/ProjectNavigation.vue'; import { useRoute, useRouter } from 'vue-router'; +import { useClipboard } from '@/composables/useClipboard'; +import { useToast } from '@/composables/useToast'; +import { useDebugInfo } from '@/composables/useDebugInfo'; export default defineComponent({ name: 'MainSidebar', @@ -295,6 +298,11 @@ export default defineComponent({ id: 'about', icon: 'info', label: this.$locale.baseText('mainSidebar.aboutN8n'), + }, + { + id: 'debugInfo', + icon: 'bug', + label: this.$locale.baseText('mainSidebar.debugInfo.buttonLabel'), position: 'bottom', }, ], @@ -389,6 +397,16 @@ export default defineComponent({ void this.cloudPlanStore.redirectToDashboard(); break; } + case 'debugInfo': { + useToast().showToast({ + title: this.$locale.baseText('mainSidebar.debugInfo.toast.title'), + message: this.$locale.baseText('mainSidebar.debugInfo.toast.message'), + type: 'info', + duration: 5000, + }); + await useClipboard().copy(useDebugInfo().generateDebugInfo()); + break; + } case 'quickstart': case 'docs': case 'forum': diff --git a/packages/editor-ui/src/composables/useDebugInfo.ts b/packages/editor-ui/src/composables/useDebugInfo.ts new file mode 100644 index 0000000000000..89e0e6f4b1585 --- /dev/null +++ b/packages/editor-ui/src/composables/useDebugInfo.ts @@ -0,0 +1,140 @@ +import { useSettingsStore } from '@/stores/settings.store'; +import type { WorkflowSettings } from 'n8n-workflow'; + +type DebugInfo = { + core: { + version: string; + platform: 'docker (cloud)' | 'docker (self-hosted)' | 'npm'; + database: 'sqlite' | 'mysql' | 'mariadb' | 'postgres'; + engine: 'regular' | 'scaling'; + license: 'community' | 'enterprise (production)' | 'enterprise (sandbox)'; + }; + storage: { + success: WorkflowSettings.SaveDataExecution; + error: WorkflowSettings.SaveDataExecution; + progress: boolean; + manual: boolean; + binary: 'memory' | 'filesystem' | 's3'; + }; + pruning: + | { + enabled: false; + } + | { + enabled: true; + maxAge: `${number} hours`; + maxCount: `${number} executions`; + }; + /** + * Reported only if insecure settings are found. + */ + security?: { + protocol?: 'http' | 'https'; + secureCookie?: boolean; + blockFileAccessToN8nFiles?: boolean; + }; +}; + +export function useDebugInfo() { + const store = useSettingsStore(); + + const coreInfo = () => { + return { + version: store.versionCli, + platform: + store.isDocker && store.deploymentType === 'cloud' + ? 'docker (cloud)' + : store.isDocker + ? 'docker (self-hosted)' + : 'npm', + database: + store.databaseType === 'postgresdb' + ? 'postgres' + : store.databaseType === 'mysqldb' + ? 'mysql' + : store.databaseType, + engine: store.isQueueModeEnabled ? 'scaling' : 'regular', + license: + store.planName === 'Community' + ? (store.planName.toLowerCase() as 'community') + : store.settings.license.environment === 'production' + ? 'enterprise (production)' + : 'enterprise (sandbox)', + } as const; + }; + + const storageInfo = (): DebugInfo['storage'] => { + return { + success: store.saveDataSuccessExecution, + error: store.saveDataErrorExecution, + progress: store.saveDataProgressExecution, + manual: store.saveManualExecutions, + binary: store.binaryDataMode === 'default' ? 'memory' : store.binaryDataMode, + }; + }; + + const pruningInfo = () => { + if (!store.pruning.isEnabled) return { enabled: false } as const; + + return { + enabled: true, + maxAge: `${store.pruning.maxAge} hours`, + maxCount: `${store.pruning.maxCount} executions`, + } as const; + }; + + const securityInfo = () => { + const info: DebugInfo['security'] = {}; + + if (store.security.protocol === 'http') info.protocol = 'http'; + if (!store.security.blockFileAccessToN8nFiles) info.blockFileAccessToN8nFiles = false; + if (!store.security.secureCookie) info.secureCookie = false; + + if (Object.keys(info).length === 0) return; + + return info; + }; + + const gatherDebugInfo = () => { + const debugInfo: DebugInfo = { + core: coreInfo(), + storage: storageInfo(), + pruning: pruningInfo(), + }; + + const security = securityInfo(); + + if (security) debugInfo.security = security; + + return debugInfo; + }; + + const toMarkdown = (debugInfo: DebugInfo): string => { + let markdown = '# Debug info\n\n'; + + for (const sectionKey in debugInfo) { + markdown += `## ${sectionKey}\n\n`; + + const section = debugInfo[sectionKey as keyof DebugInfo]; + + if (!section) continue; + + for (const itemKey in section) { + const itemValue = section[itemKey as keyof typeof section]; + markdown += `- ${itemKey}: ${itemValue}\n`; + } + + markdown += '\n'; + } + + return markdown; + }; + + const generateDebugInfo = () => { + return toMarkdown(gatherDebugInfo()); + }; + + return { + generateDebugInfo, + }; +} diff --git a/packages/editor-ui/src/plugins/i18n/locales/en.json b/packages/editor-ui/src/plugins/i18n/locales/en.json index 16eebd55d9761..e5a333767bf76 100644 --- a/packages/editor-ui/src/plugins/i18n/locales/en.json +++ b/packages/editor-ui/src/plugins/i18n/locales/en.json @@ -828,6 +828,9 @@ "readOnlyEnv.showMessage.workflows.message": "Workflows are read-only in protected instances.", "readOnlyEnv.showMessage.workflows.title": "Cannot edit workflow", "mainSidebar.aboutN8n": "About n8n", + "mainSidebar.debugInfo.buttonLabel": "Debug info", + "mainSidebar.debugInfo.toast.title": "Debug info", + "mainSidebar.debugInfo.toast.message": "Copied debug info to clipboard", "mainSidebar.confirmMessage.workflowDelete.cancelButtonText": "", "mainSidebar.confirmMessage.workflowDelete.confirmButtonText": "Yes, delete", "mainSidebar.confirmMessage.workflowDelete.headline": "Delete Workflow?", diff --git a/packages/editor-ui/src/stores/settings.store.ts b/packages/editor-ui/src/stores/settings.store.ts index d0f9be44a20ef..249a959f984ec 100644 --- a/packages/editor-ui/src/stores/settings.store.ts +++ b/packages/editor-ui/src/stores/settings.store.ts @@ -68,12 +68,40 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, { saveDataErrorExecution: 'all', saveDataSuccessExecution: 'all', saveManualExecutions: false, + saveDataProgressExecution: false, }), getters: { + isDocker(): boolean { + return this.settings.isDocker; + }, + databaseType(): 'sqlite' | 'mariadb' | 'mysqldb' | 'postgresdb' { + return this.settings.databaseType; + }, + planName(): string { + return this.settings.license.planName ?? 'Community'; + }, + binaryDataMode(): 'default' | 'filesystem' | 's3' { + return this.settings.binaryDataMode; + }, + pruning(): { isEnabled: boolean; maxAge: number; maxCount: number } { + return this.settings.pruning; + }, + security(): { + protocol: 'http' | 'https'; + blockFileAccessToN8nFiles: boolean; + secureCookie: boolean; + } { + return { + protocol: this.settings.security.protocol, + blockFileAccessToN8nFiles: this.settings.security.blockFileAccessToN8nFiles, + secureCookie: this.settings.authCookie.secure, + }; + }, isEnterpriseFeatureEnabled() { return (feature: EnterpriseEditionFeatureValue): boolean => Boolean(this.settings.enterprise?.[feature]); }, + versionCli(): string { return this.settings.versionCli; }, @@ -269,6 +297,7 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, { this.setAllowedModules(settings.allowedModules); this.setSaveDataErrorExecution(settings.saveDataErrorExecution); this.setSaveDataSuccessExecution(settings.saveDataSuccessExecution); + this.setSaveDataProgressExecution(settings.saveExecutionProgress); this.setSaveManualExecutions(settings.saveManualExecutions); rootStore.setUrlBaseWebhook(settings.urlBaseWebhook); @@ -357,15 +386,18 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, { const rootStore = useRootStore(); return await runLdapSync(rootStore.restApiContext, data); }, - setSaveDataErrorExecution(newValue: string) { + setSaveDataErrorExecution(newValue: WorkflowSettings.SaveDataExecution) { this.saveDataErrorExecution = newValue; }, - setSaveDataSuccessExecution(newValue: string) { + setSaveDataSuccessExecution(newValue: WorkflowSettings.SaveDataExecution) { this.saveDataSuccessExecution = newValue; }, setSaveManualExecutions(saveManualExecutions: boolean) { this.saveManualExecutions = saveManualExecutions; }, + setSaveDataProgressExecution(newValue: boolean) { + this.saveDataProgressExecution = newValue; + }, async getTimezones(): Promise { const rootStore = useRootStore(); return await makeRestApiRequest(rootStore.restApiContext, 'GET', '/options/timezones'); diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index 564fc498ba7e1..7e814ad7265ed 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -2557,6 +2557,8 @@ export type ExpressionEvaluatorType = 'tmpl' | 'tournament'; export type N8nAIProviderType = 'openai' | 'unknown'; export interface IN8nUISettings { + isDocker: boolean; + databaseType: 'sqlite' | 'mariadb' | 'mysqldb' | 'postgresdb'; endpointForm: string; endpointFormTest: string; endpointFormWaiting: string; @@ -2565,6 +2567,7 @@ export interface IN8nUISettings { saveDataErrorExecution: WorkflowSettings.SaveDataExecution; saveDataSuccessExecution: WorkflowSettings.SaveDataExecution; saveManualExecutions: boolean; + saveExecutionProgress: boolean; executionTimeout: number; maxExecutionTimeout: number; workflowCallerPolicyDefaultOption: WorkflowSettings.CallerPolicy; @@ -2579,7 +2582,7 @@ export interface IN8nUISettings { authCookie: { secure: boolean; }; - binaryDataMode: string; + binaryDataMode: 'default' | 'filesystem' | 's3'; releaseChannel: 'stable' | 'beta' | 'nightly' | 'dev'; n8nMetadata?: { userId?: string; @@ -2655,6 +2658,7 @@ export interface IN8nUISettings { }; hideUsagePage: boolean; license: { + planName?: string; environment: 'development' | 'production' | 'staging'; }; variables: { @@ -2680,6 +2684,15 @@ export interface IN8nUISettings { pruneTime: number; licensePruneTime: number; }; + pruning: { + isEnabled: boolean; + maxAge: number; + maxCount: number; + }; + security?: { + protocol: 'http' | 'https'; + blockFileAccessToN8nFiles: boolean; + }; } export interface SecretsHelpersBase { From 32c2de15b1901283a5e3f1851af9e3292c17d75c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Mon, 1 Jul 2024 10:40:15 +0200 Subject: [PATCH 02/12] Fix typecheck --- packages/editor-ui/src/__tests__/defaults.ts | 4 ++++ packages/editor-ui/src/__tests__/utils.ts | 1 + packages/workflow/src/Interfaces.ts | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/editor-ui/src/__tests__/defaults.ts b/packages/editor-ui/src/__tests__/defaults.ts index 249a9538fd8bc..72068f70466c6 100644 --- a/packages/editor-ui/src/__tests__/defaults.ts +++ b/packages/editor-ui/src/__tests__/defaults.ts @@ -121,4 +121,8 @@ export const defaultSettings: IN8nUISettings = { pruneTime: 0, licensePruneTime: 0, }, + security: { + protocol: 'https', + blockFileAccessToN8nFiles: false, + }, }; diff --git a/packages/editor-ui/src/__tests__/utils.ts b/packages/editor-ui/src/__tests__/utils.ts index 7f6e14e24fab1..4e94ec57f429d 100644 --- a/packages/editor-ui/src/__tests__/utils.ts +++ b/packages/editor-ui/src/__tests__/utils.ts @@ -68,6 +68,7 @@ export const SETTINGS_STORE_DEFAULT_STATE: ISettingsState = { onboardingCallPromptEnabled: false, saveDataErrorExecution: 'all', saveDataSuccessExecution: 'all', + saveDataProgressExecution: false, saveManualExecutions: false, }; diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index 7e814ad7265ed..7b9f3fc83ba1c 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -2689,7 +2689,7 @@ export interface IN8nUISettings { maxAge: number; maxCount: number; }; - security?: { + security: { protocol: 'http' | 'https'; blockFileAccessToN8nFiles: boolean; }; From 88dd42520d67bcea4da0c81b6c3cf90b0751ea25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Mon, 1 Jul 2024 12:20:23 +0200 Subject: [PATCH 03/12] Remove `protocol` --- packages/cli/src/services/frontend.service.ts | 1 - packages/editor-ui/src/composables/useDebugInfo.ts | 2 -- packages/editor-ui/src/stores/settings.store.ts | 2 -- packages/workflow/src/Interfaces.ts | 1 - 4 files changed, 6 deletions(-) diff --git a/packages/cli/src/services/frontend.service.ts b/packages/cli/src/services/frontend.service.ts index 8c4ef1e1aae2c..830755887bf26 100644 --- a/packages/cli/src/services/frontend.service.ts +++ b/packages/cli/src/services/frontend.service.ts @@ -228,7 +228,6 @@ export class FrontendService { maxCount: config.getEnv('executions.pruneDataMaxCount'), }, security: { - protocol: config.getEnv('protocol'), blockFileAccessToN8nFiles: config.getEnv('security.blockFileAccessToN8nFiles'), }, }; diff --git a/packages/editor-ui/src/composables/useDebugInfo.ts b/packages/editor-ui/src/composables/useDebugInfo.ts index 89e0e6f4b1585..7b8760a61f2bb 100644 --- a/packages/editor-ui/src/composables/useDebugInfo.ts +++ b/packages/editor-ui/src/composables/useDebugInfo.ts @@ -29,7 +29,6 @@ type DebugInfo = { * Reported only if insecure settings are found. */ security?: { - protocol?: 'http' | 'https'; secureCookie?: boolean; blockFileAccessToN8nFiles?: boolean; }; @@ -86,7 +85,6 @@ export function useDebugInfo() { const securityInfo = () => { const info: DebugInfo['security'] = {}; - if (store.security.protocol === 'http') info.protocol = 'http'; if (!store.security.blockFileAccessToN8nFiles) info.blockFileAccessToN8nFiles = false; if (!store.security.secureCookie) info.secureCookie = false; diff --git a/packages/editor-ui/src/stores/settings.store.ts b/packages/editor-ui/src/stores/settings.store.ts index 249a959f984ec..2c5d040a49f6f 100644 --- a/packages/editor-ui/src/stores/settings.store.ts +++ b/packages/editor-ui/src/stores/settings.store.ts @@ -87,12 +87,10 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, { return this.settings.pruning; }, security(): { - protocol: 'http' | 'https'; blockFileAccessToN8nFiles: boolean; secureCookie: boolean; } { return { - protocol: this.settings.security.protocol, blockFileAccessToN8nFiles: this.settings.security.blockFileAccessToN8nFiles, secureCookie: this.settings.authCookie.secure, }; diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index 7b9f3fc83ba1c..2eaadacbc2e20 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -2690,7 +2690,6 @@ export interface IN8nUISettings { maxCount: number; }; security: { - protocol: 'http' | 'https'; blockFileAccessToN8nFiles: boolean; }; } From 433f9aaf16236afbf61b49ecbbff662d69ab4e9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Mon, 1 Jul 2024 12:26:40 +0200 Subject: [PATCH 04/12] Append timestamp --- packages/editor-ui/src/composables/useDebugInfo.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/editor-ui/src/composables/useDebugInfo.ts b/packages/editor-ui/src/composables/useDebugInfo.ts index 7b8760a61f2bb..cca0f51dcdd98 100644 --- a/packages/editor-ui/src/composables/useDebugInfo.ts +++ b/packages/editor-ui/src/composables/useDebugInfo.ts @@ -6,7 +6,7 @@ type DebugInfo = { version: string; platform: 'docker (cloud)' | 'docker (self-hosted)' | 'npm'; database: 'sqlite' | 'mysql' | 'mariadb' | 'postgres'; - engine: 'regular' | 'scaling'; + executionMode: 'regular' | 'scaling'; license: 'community' | 'enterprise (production)' | 'enterprise (sandbox)'; }; storage: { @@ -52,7 +52,7 @@ export function useDebugInfo() { : store.databaseType === 'mysqldb' ? 'mysql' : store.databaseType, - engine: store.isQueueModeEnabled ? 'scaling' : 'regular', + executionMode: store.isQueueModeEnabled ? 'scaling' : 'regular', license: store.planName === 'Community' ? (store.planName.toLowerCase() as 'community') @@ -128,8 +128,12 @@ export function useDebugInfo() { return markdown; }; + const appendTimestamp = (markdown: string) => { + return `${markdown}Generated at: ${new Date().toISOString()}`; + }; + const generateDebugInfo = () => { - return toMarkdown(gatherDebugInfo()); + return appendTimestamp(toMarkdown(gatherDebugInfo())); }; return { From 532639b206249f913114d9c1af3855fa887c8062 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Mon, 1 Jul 2024 12:34:42 +0200 Subject: [PATCH 05/12] Add `nodeJsVersion` --- packages/cli/src/services/frontend.service.ts | 3 +++ packages/editor-ui/src/composables/useDebugInfo.ts | 6 ++++-- packages/editor-ui/src/stores/settings.store.ts | 5 +++++ packages/workflow/src/Interfaces.ts | 1 + 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/services/frontend.service.ts b/packages/cli/src/services/frontend.service.ts index 830755887bf26..cb63638bceb2d 100644 --- a/packages/cli/src/services/frontend.service.ts +++ b/packages/cli/src/services/frontend.service.ts @@ -103,6 +103,7 @@ export class FrontendService { urlBaseWebhook: this.urlService.getWebhookBaseUrl(), urlBaseEditor: instanceBaseUrl, binaryDataMode: config.getEnv('binaryDataManager.mode'), + nodeJsVersion: process.version.replace(/^v/, ''), versionCli: '', authCookie: { secure: config.getEnv('secure_cookie'), @@ -231,6 +232,8 @@ export class FrontendService { blockFileAccessToN8nFiles: config.getEnv('security.blockFileAccessToN8nFiles'), }, }; + + console.log('process.version', this.settings.nodeJsVersion); } async generateTypes() { diff --git a/packages/editor-ui/src/composables/useDebugInfo.ts b/packages/editor-ui/src/composables/useDebugInfo.ts index cca0f51dcdd98..8be4e3342ce60 100644 --- a/packages/editor-ui/src/composables/useDebugInfo.ts +++ b/packages/editor-ui/src/composables/useDebugInfo.ts @@ -3,8 +3,9 @@ import type { WorkflowSettings } from 'n8n-workflow'; type DebugInfo = { core: { - version: string; + n8nVersion: string; platform: 'docker (cloud)' | 'docker (self-hosted)' | 'npm'; + nodeJsVersion: string; database: 'sqlite' | 'mysql' | 'mariadb' | 'postgres'; executionMode: 'regular' | 'scaling'; license: 'community' | 'enterprise (production)' | 'enterprise (sandbox)'; @@ -39,13 +40,14 @@ export function useDebugInfo() { const coreInfo = () => { return { - version: store.versionCli, + n8nVersion: store.versionCli, platform: store.isDocker && store.deploymentType === 'cloud' ? 'docker (cloud)' : store.isDocker ? 'docker (self-hosted)' : 'npm', + nodeJsVersion: store.nodeJsVersion, database: store.databaseType === 'postgresdb' ? 'postgres' diff --git a/packages/editor-ui/src/stores/settings.store.ts b/packages/editor-ui/src/stores/settings.store.ts index 2c5d040a49f6f..f96405e144998 100644 --- a/packages/editor-ui/src/stores/settings.store.ts +++ b/packages/editor-ui/src/stores/settings.store.ts @@ -103,6 +103,9 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, { versionCli(): string { return this.settings.versionCli; }, + nodeJsVersion(): string { + return this.settings.nodeJsVersion; + }, isPublicApiEnabled(): boolean { return this.api.enabled; }, @@ -272,6 +275,8 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, { useRootStore().setVersionCli(settings.versionCli); } + console.log('this.settings', this.settings); + if ( settings.authCookie.secure && location.protocol === 'http:' && diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index 2eaadacbc2e20..5e119be7a1c92 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -2579,6 +2579,7 @@ export interface IN8nUISettings { urlBaseWebhook: string; urlBaseEditor: string; versionCli: string; + nodeJsVersion: string; authCookie: { secure: boolean; }; From 46d4e57d1f3812133e596c6f417fc7c8e961021b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Mon, 1 Jul 2024 12:39:51 +0200 Subject: [PATCH 06/12] Add concurrency --- packages/cli/src/services/frontend.service.ts | 3 +-- packages/editor-ui/src/composables/useDebugInfo.ts | 2 ++ packages/editor-ui/src/stores/settings.store.ts | 3 +++ packages/workflow/src/Interfaces.ts | 1 + 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/services/frontend.service.ts b/packages/cli/src/services/frontend.service.ts index cb63638bceb2d..e4ea1798a4ad0 100644 --- a/packages/cli/src/services/frontend.service.ts +++ b/packages/cli/src/services/frontend.service.ts @@ -105,6 +105,7 @@ export class FrontendService { binaryDataMode: config.getEnv('binaryDataManager.mode'), nodeJsVersion: process.version.replace(/^v/, ''), versionCli: '', + concurrency: config.getEnv('executions.concurrency.productionLimit'), authCookie: { secure: config.getEnv('secure_cookie'), }, @@ -232,8 +233,6 @@ export class FrontendService { blockFileAccessToN8nFiles: config.getEnv('security.blockFileAccessToN8nFiles'), }, }; - - console.log('process.version', this.settings.nodeJsVersion); } async generateTypes() { diff --git a/packages/editor-ui/src/composables/useDebugInfo.ts b/packages/editor-ui/src/composables/useDebugInfo.ts index 8be4e3342ce60..6c968e9d020ac 100644 --- a/packages/editor-ui/src/composables/useDebugInfo.ts +++ b/packages/editor-ui/src/composables/useDebugInfo.ts @@ -9,6 +9,7 @@ type DebugInfo = { database: 'sqlite' | 'mysql' | 'mariadb' | 'postgres'; executionMode: 'regular' | 'scaling'; license: 'community' | 'enterprise (production)' | 'enterprise (sandbox)'; + concurrency: number; }; storage: { success: WorkflowSettings.SaveDataExecution; @@ -55,6 +56,7 @@ export function useDebugInfo() { ? 'mysql' : store.databaseType, executionMode: store.isQueueModeEnabled ? 'scaling' : 'regular', + concurrency: store.settings.concurrency, license: store.planName === 'Community' ? (store.planName.toLowerCase() as 'community') diff --git a/packages/editor-ui/src/stores/settings.store.ts b/packages/editor-ui/src/stores/settings.store.ts index f96405e144998..fa4bee90c3ab6 100644 --- a/packages/editor-ui/src/stores/settings.store.ts +++ b/packages/editor-ui/src/stores/settings.store.ts @@ -106,6 +106,9 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, { nodeJsVersion(): string { return this.settings.nodeJsVersion; }, + concurrency(): number { + return this.settings.concurrency; + }, isPublicApiEnabled(): boolean { return this.api.enabled; }, diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index 5e119be7a1c92..82080c660721c 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -2580,6 +2580,7 @@ export interface IN8nUISettings { urlBaseEditor: string; versionCli: string; nodeJsVersion: string; + concurrency: number; authCookie: { secure: boolean; }; From f31d6bc1f4677bc87d0bbf29f71173161687b50a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Mon, 1 Jul 2024 12:41:06 +0200 Subject: [PATCH 07/12] Rename `binary` to `binaryMode` --- packages/editor-ui/src/composables/useDebugInfo.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/editor-ui/src/composables/useDebugInfo.ts b/packages/editor-ui/src/composables/useDebugInfo.ts index 6c968e9d020ac..4c1586da78588 100644 --- a/packages/editor-ui/src/composables/useDebugInfo.ts +++ b/packages/editor-ui/src/composables/useDebugInfo.ts @@ -16,7 +16,7 @@ type DebugInfo = { error: WorkflowSettings.SaveDataExecution; progress: boolean; manual: boolean; - binary: 'memory' | 'filesystem' | 's3'; + binaryMode: 'memory' | 'filesystem' | 's3'; }; pruning: | { @@ -72,7 +72,7 @@ export function useDebugInfo() { error: store.saveDataErrorExecution, progress: store.saveDataProgressExecution, manual: store.saveManualExecutions, - binary: store.binaryDataMode === 'default' ? 'memory' : store.binaryDataMode, + binaryMode: store.binaryDataMode === 'default' ? 'memory' : store.binaryDataMode, }; }; From 8c8c42f68f78469dda5c8ec0e617371477efff7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Mon, 1 Jul 2024 12:53:19 +0200 Subject: [PATCH 08/12] Fix typecheck --- packages/editor-ui/src/__tests__/defaults.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/editor-ui/src/__tests__/defaults.ts b/packages/editor-ui/src/__tests__/defaults.ts index 72068f70466c6..bf9914e699fc3 100644 --- a/packages/editor-ui/src/__tests__/defaults.ts +++ b/packages/editor-ui/src/__tests__/defaults.ts @@ -89,6 +89,8 @@ export const defaultSettings: IN8nUISettings = { quota: 10, }, versionCli: '', + nodeJsVersion: '', + concurrency: -1, versionNotifications: { enabled: true, endpoint: '', @@ -122,7 +124,6 @@ export const defaultSettings: IN8nUISettings = { licensePruneTime: 0, }, security: { - protocol: 'https', blockFileAccessToN8nFiles: false, }, }; From e538bb8fd2a147c57eef21b28e767b471c0126cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Mon, 1 Jul 2024 12:58:45 +0200 Subject: [PATCH 09/12] Remove log --- packages/editor-ui/src/stores/settings.store.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/editor-ui/src/stores/settings.store.ts b/packages/editor-ui/src/stores/settings.store.ts index fa4bee90c3ab6..d8e23f7e31453 100644 --- a/packages/editor-ui/src/stores/settings.store.ts +++ b/packages/editor-ui/src/stores/settings.store.ts @@ -278,8 +278,6 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, { useRootStore().setVersionCli(settings.versionCli); } - console.log('this.settings', this.settings); - if ( settings.authCookie.secure && location.protocol === 'http:' && From 28df5a9e39b431d2e7b1be9915551baa36ba305e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Mon, 1 Jul 2024 15:30:36 +0200 Subject: [PATCH 10/12] Move button to about modal --- .../editor-ui/src/components/AboutModal.vue | 22 +++++++++++++++++++ .../editor-ui/src/components/MainSidebar.vue | 6 ----- .../src/plugins/i18n/locales/en.json | 7 +++--- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/packages/editor-ui/src/components/AboutModal.vue b/packages/editor-ui/src/components/AboutModal.vue index 1b61aabf67dc5..1aeeed0fb5715 100644 --- a/packages/editor-ui/src/components/AboutModal.vue +++ b/packages/editor-ui/src/components/AboutModal.vue @@ -42,6 +42,16 @@ {{ rootStore.instanceId }} + + + {{ $locale.baseText('about.debug.title') }} + + +
+ {{ $locale.baseText('about.debug.message') }} +
+
+
@@ -66,6 +76,9 @@ import Modal from './Modal.vue'; import { ABOUT_MODAL_KEY } from '../constants'; import { useSettingsStore } from '@/stores/settings.store'; import { useRootStore } from '@/stores/root.store'; +import { useToast } from '@/composables/useToast'; +import { useClipboard } from '@/composables/useClipboard'; +import { useDebugInfo } from '@/composables/useDebugInfo'; export default defineComponent({ name: 'About', @@ -85,6 +98,15 @@ export default defineComponent({ closeDialog() { this.modalBus.emit('close'); }, + async copyDebugInfoToClipboard() { + useToast().showToast({ + title: this.$locale.baseText('about.debug.toast.title'), + message: this.$locale.baseText('about.debug.toast.message'), + type: 'info', + duration: 5000, + }); + await useClipboard().copy(useDebugInfo().generateDebugInfo()); + }, }, }); diff --git a/packages/editor-ui/src/components/MainSidebar.vue b/packages/editor-ui/src/components/MainSidebar.vue index 5bbd6440bc7fb..8175accc2c6f4 100644 --- a/packages/editor-ui/src/components/MainSidebar.vue +++ b/packages/editor-ui/src/components/MainSidebar.vue @@ -299,12 +299,6 @@ export default defineComponent({ icon: 'info', label: this.$locale.baseText('mainSidebar.aboutN8n'), }, - { - id: 'debugInfo', - icon: 'bug', - label: this.$locale.baseText('mainSidebar.debugInfo.buttonLabel'), - position: 'bottom', - }, ], }, ]; diff --git a/packages/editor-ui/src/plugins/i18n/locales/en.json b/packages/editor-ui/src/plugins/i18n/locales/en.json index e5a333767bf76..ead985dcd2d53 100644 --- a/packages/editor-ui/src/plugins/i18n/locales/en.json +++ b/packages/editor-ui/src/plugins/i18n/locales/en.json @@ -78,6 +78,10 @@ "about.n8nVersion": "n8n Version", "about.sourceCode": "Source Code", "about.instanceID": "Instance ID", + "about.debug.title": "Debug", + "about.debug.message": "Copy debug information", + "about.debug.toast.title": "Debug info", + "about.debug.toast.message": "Copied debug info to clipboard", "askAi.dialog.title": "'Ask AI' is almost ready", "askAi.dialog.body": "We’re still applying the finishing touches. Soon, you will be able to automatically generate code from simple text prompts. Join the waitlist to get early access to this feature.", "askAi.dialog.signup": "Join Waitlist", @@ -828,9 +832,6 @@ "readOnlyEnv.showMessage.workflows.message": "Workflows are read-only in protected instances.", "readOnlyEnv.showMessage.workflows.title": "Cannot edit workflow", "mainSidebar.aboutN8n": "About n8n", - "mainSidebar.debugInfo.buttonLabel": "Debug info", - "mainSidebar.debugInfo.toast.title": "Debug info", - "mainSidebar.debugInfo.toast.message": "Copied debug info to clipboard", "mainSidebar.confirmMessage.workflowDelete.cancelButtonText": "", "mainSidebar.confirmMessage.workflowDelete.confirmButtonText": "Yes, delete", "mainSidebar.confirmMessage.workflowDelete.headline": "Delete Workflow?", From a3aa3d84924935f47faad2aefb19576c76662adb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Mon, 1 Jul 2024 15:32:06 +0200 Subject: [PATCH 11/12] Remove leftover --- packages/editor-ui/src/components/MainSidebar.vue | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/packages/editor-ui/src/components/MainSidebar.vue b/packages/editor-ui/src/components/MainSidebar.vue index 8175accc2c6f4..c66e0a89b5c68 100644 --- a/packages/editor-ui/src/components/MainSidebar.vue +++ b/packages/editor-ui/src/components/MainSidebar.vue @@ -128,9 +128,6 @@ import { useDebounce } from '@/composables/useDebounce'; import { useBecomeTemplateCreatorStore } from '@/components/BecomeTemplateCreatorCta/becomeTemplateCreatorStore'; import ProjectNavigation from '@/components/Projects/ProjectNavigation.vue'; import { useRoute, useRouter } from 'vue-router'; -import { useClipboard } from '@/composables/useClipboard'; -import { useToast } from '@/composables/useToast'; -import { useDebugInfo } from '@/composables/useDebugInfo'; export default defineComponent({ name: 'MainSidebar', @@ -298,6 +295,7 @@ export default defineComponent({ id: 'about', icon: 'info', label: this.$locale.baseText('mainSidebar.aboutN8n'), + position: 'bottom', }, ], }, @@ -391,16 +389,6 @@ export default defineComponent({ void this.cloudPlanStore.redirectToDashboard(); break; } - case 'debugInfo': { - useToast().showToast({ - title: this.$locale.baseText('mainSidebar.debugInfo.toast.title'), - message: this.$locale.baseText('mainSidebar.debugInfo.toast.message'), - type: 'info', - duration: 5000, - }); - await useClipboard().copy(useDebugInfo().generateDebugInfo()); - break; - } case 'quickstart': case 'docs': case 'forum': From 8de79ab0688378398bc6cb7840692d1139d51e10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Mon, 1 Jul 2024 18:42:04 +0200 Subject: [PATCH 12/12] Add `consumerId`` --- packages/cli/src/License.ts | 4 ++++ packages/cli/src/services/frontend.service.ts | 2 ++ packages/editor-ui/src/__tests__/defaults.ts | 2 +- packages/editor-ui/src/composables/useDebugInfo.ts | 2 ++ packages/editor-ui/src/stores/settings.store.ts | 3 +++ packages/workflow/src/Interfaces.ts | 1 + 6 files changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/License.ts b/packages/cli/src/License.ts index ab61e257ec5af..e07b5481582f1 100644 --- a/packages/cli/src/License.ts +++ b/packages/cli/src/License.ts @@ -338,6 +338,10 @@ export class License { ); } + getConsumerId() { + return this.manager?.getConsumerId() ?? 'unknown'; + } + // Helper functions for computed data getUsersLimit() { return this.getFeatureValue(LICENSE_QUOTAS.USERS_LIMIT) ?? UNLIMITED_LICENSE_QUOTA; diff --git a/packages/cli/src/services/frontend.service.ts b/packages/cli/src/services/frontend.service.ts index e4ea1798a4ad0..0f7a9167e16cc 100644 --- a/packages/cli/src/services/frontend.service.ts +++ b/packages/cli/src/services/frontend.service.ts @@ -202,6 +202,7 @@ export class FrontendService { }, hideUsagePage: config.getEnv('hideUsagePage'), license: { + consumerId: 'unknown', environment: config.getEnv('license.tenantId') === 1 ? 'production' : 'staging', }, variables: { @@ -284,6 +285,7 @@ export class FrontendService { const isS3Licensed = this.license.isBinaryDataS3Licensed(); this.settings.license.planName = this.license.getPlanName(); + this.settings.license.consumerId = this.license.getConsumerId(); // refresh enterprise status Object.assign(this.settings.enterprise, { diff --git a/packages/editor-ui/src/__tests__/defaults.ts b/packages/editor-ui/src/__tests__/defaults.ts index bf9914e699fc3..ec67a2517f961 100644 --- a/packages/editor-ui/src/__tests__/defaults.ts +++ b/packages/editor-ui/src/__tests__/defaults.ts @@ -47,7 +47,7 @@ export const defaultSettings: IN8nUISettings = { hiringBannerEnabled: false, instanceId: '', isNpmAvailable: false, - license: { environment: 'development' }, + license: { environment: 'development', consumerId: 'unknown' }, logLevel: 'info', maxExecutionTimeout: 0, oauthCallbackUrls: { oauth1: '', oauth2: '' }, diff --git a/packages/editor-ui/src/composables/useDebugInfo.ts b/packages/editor-ui/src/composables/useDebugInfo.ts index 4c1586da78588..97ca75b1ee673 100644 --- a/packages/editor-ui/src/composables/useDebugInfo.ts +++ b/packages/editor-ui/src/composables/useDebugInfo.ts @@ -9,6 +9,7 @@ type DebugInfo = { database: 'sqlite' | 'mysql' | 'mariadb' | 'postgres'; executionMode: 'regular' | 'scaling'; license: 'community' | 'enterprise (production)' | 'enterprise (sandbox)'; + consumerId: string; concurrency: number; }; storage: { @@ -63,6 +64,7 @@ export function useDebugInfo() { : store.settings.license.environment === 'production' ? 'enterprise (production)' : 'enterprise (sandbox)', + consumerId: store.consumerId, } as const; }; diff --git a/packages/editor-ui/src/stores/settings.store.ts b/packages/editor-ui/src/stores/settings.store.ts index d8e23f7e31453..6e9ae2bbdbab4 100644 --- a/packages/editor-ui/src/stores/settings.store.ts +++ b/packages/editor-ui/src/stores/settings.store.ts @@ -80,6 +80,9 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, { planName(): string { return this.settings.license.planName ?? 'Community'; }, + consumerId(): string { + return this.settings.license.consumerId; + }, binaryDataMode(): 'default' | 'filesystem' | 's3' { return this.settings.binaryDataMode; }, diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index 82080c660721c..ef28fcd95f074 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -2661,6 +2661,7 @@ export interface IN8nUISettings { hideUsagePage: boolean; license: { planName?: string; + consumerId: string; environment: 'development' | 'production' | 'staging'; }; variables: {