Files
n8n-enterprise-unlocked/packages/frontend/editor-ui/src/composables/useDebugInfo.ts
2025-04-25 09:40:45 +02:00

185 lines
4.7 KiB
TypeScript

import { useRootStore } from '@/stores/root.store';
import { useSettingsStore } from '@/stores/settings.store';
import { useDeviceSupport } from '@n8n/composables/useDeviceSupport';
import type { WorkflowSettings } from 'n8n-workflow';
type DebugInfo = {
core: {
n8nVersion: string;
platform: 'docker (cloud)' | 'docker (self-hosted)' | 'npm';
nodeJsVersion: string;
database: 'sqlite' | 'mysql' | 'mariadb' | 'postgres';
executionMode: 'regular' | 'scaling (single-main)' | 'scaling (multi-main)';
license: 'community' | 'enterprise (production)' | 'enterprise (sandbox)';
consumerId?: string;
concurrency: number;
};
storage: {
success: WorkflowSettings.SaveDataExecution;
error: WorkflowSettings.SaveDataExecution;
progress: boolean;
manual: boolean;
binaryMode: 'memory' | 'filesystem' | 's3';
};
pruning:
| {
enabled: false;
}
| {
enabled: true;
maxAge: `${number} hours`;
maxCount: `${number} executions`;
};
/**
* Reported only if insecure settings are found.
*/
security?: {
secureCookie?: boolean;
blockFileAccessToN8nFiles?: boolean;
};
client: {
userAgent: string;
isTouchDevice: boolean;
};
};
export function useDebugInfo() {
const settingsStore = useSettingsStore();
const rootStore = useRootStore();
const { isTouchDevice, userAgent } = useDeviceSupport();
const coreInfo = (skipSensitive?: boolean) => {
const info = {
n8nVersion: rootStore.versionCli,
platform:
settingsStore.isDocker && settingsStore.deploymentType === 'cloud'
? 'docker (cloud)'
: settingsStore.isDocker
? 'docker (self-hosted)'
: 'npm',
nodeJsVersion: settingsStore.nodeJsVersion,
database:
settingsStore.databaseType === 'postgresdb'
? 'postgres'
: settingsStore.databaseType === 'mysqldb'
? 'mysql'
: settingsStore.databaseType,
executionMode: settingsStore.isQueueModeEnabled
? settingsStore.isMultiMain
? 'scaling (multi-main)'
: 'scaling (single-main)'
: 'regular',
concurrency: settingsStore.settings.concurrency,
license:
settingsStore.isCommunityPlan || !settingsStore.settings.license
? 'community'
: settingsStore.settings.license.environment === 'production'
? 'enterprise (production)'
: 'enterprise (sandbox)',
} as const;
if (!skipSensitive) {
return {
...info,
consumerId: !skipSensitive ? settingsStore.consumerId : undefined,
};
}
return info;
};
const storageInfo = (): DebugInfo['storage'] => {
return {
success: settingsStore.saveDataSuccessExecution,
error: settingsStore.saveDataErrorExecution,
progress: settingsStore.saveDataProgressExecution,
manual: settingsStore.saveManualExecutions,
binaryMode:
settingsStore.binaryDataMode === 'default' ? 'memory' : settingsStore.binaryDataMode,
};
};
const pruningInfo = () => {
if (!settingsStore.pruning?.isEnabled) return { enabled: false } as const;
return {
enabled: true,
maxAge: `${settingsStore.pruning?.maxAge} hours`,
maxCount: `${settingsStore.pruning?.maxCount} executions`,
} as const;
};
const securityInfo = () => {
const info: DebugInfo['security'] = {};
if (!settingsStore.security.blockFileAccessToN8nFiles) info.blockFileAccessToN8nFiles = false;
if (!settingsStore.security.secureCookie) info.secureCookie = false;
if (Object.keys(info).length === 0) return;
return info;
};
const client = (): DebugInfo['client'] => {
return {
userAgent,
isTouchDevice,
};
};
const gatherDebugInfo = (skipSensitive?: boolean) => {
const debugInfo: DebugInfo = {
core: coreInfo(skipSensitive),
storage: storageInfo(),
pruning: pruningInfo(),
client: client(),
};
const security = securityInfo();
if (security) debugInfo.security = security;
return debugInfo;
};
const toMarkdown = (
debugInfo: DebugInfo,
{ secondaryHeader }: { secondaryHeader?: boolean },
): string => {
const extraLevel = secondaryHeader ? '#' : '';
let markdown = `${extraLevel}# Debug info\n\n`;
for (const sectionKey in debugInfo) {
markdown += `${extraLevel}## ${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 appendTimestamp = (markdown: string) => {
return `${markdown}Generated at: ${new Date().toISOString()}`;
};
const generateDebugInfo = ({
skipSensitive,
secondaryHeader,
}: { skipSensitive?: boolean; secondaryHeader?: boolean } = {}) => {
return appendTimestamp(toMarkdown(gatherDebugInfo(skipSensitive), { secondaryHeader }));
};
return {
generateDebugInfo,
};
}