mirror of
https://github.com/n8n-io/n8n.git
synced 2025-11-20 17:46:34 +00:00
chore: Remove self-install telemetry (#21988)
This commit is contained in:
parent
3d3e8ccf1d
commit
bf1511ae57
@ -3,9 +3,9 @@ import AssistantsHub from '@/features/ai/assistant/components/AssistantsHub.vue'
|
||||
import AskAssistantFloatingButton from '@/features/ai/assistant/components/Chat/AskAssistantFloatingButton.vue';
|
||||
import BannerStack from '@/features/shared/banners/components/BannerStack.vue';
|
||||
import Modals from '@/app/components/Modals.vue';
|
||||
import Telemetry from '@/app/components/Telemetry.vue';
|
||||
import { useHistoryHelper } from '@/app/composables/useHistoryHelper';
|
||||
import { useTelemetryContext } from '@/app/composables/useTelemetryContext';
|
||||
import { useTelemetryInitializer } from '@/app/composables/useTelemetryInitializer';
|
||||
import { useWorkflowDiffRouting } from '@/app/composables/useWorkflowDiffRouting';
|
||||
import {
|
||||
APP_MODALS_ELEMENT_ID,
|
||||
@ -63,6 +63,8 @@ useHistoryHelper(route);
|
||||
// Initialize workflow diff routing management
|
||||
useWorkflowDiffRouting();
|
||||
|
||||
useTelemetryInitializer();
|
||||
|
||||
const loading = ref(true);
|
||||
const defaultLocale = computed(() => rootStore.defaultLocale);
|
||||
const isDemoMode = computed(() => route.name === VIEWS.DEMO);
|
||||
@ -181,7 +183,6 @@ useExposeCssVar('--ask-assistant--floating-button--margin-bottom', askAiFloating
|
||||
@input-change="onCommandBarChange"
|
||||
@navigate-to="onCommandBarNavigateTo"
|
||||
/>
|
||||
<Telemetry />
|
||||
<AskAssistantFloatingButton v-if="assistantStore.isFloatingButtonShown" />
|
||||
</div>
|
||||
<AssistantsHub />
|
||||
|
||||
@ -1,127 +0,0 @@
|
||||
import { useRoute } from 'vue-router';
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import { createComponentRenderer } from '@/__tests__/render';
|
||||
import type { MockedStore } from '@/__tests__/utils';
|
||||
import { mockedStore } from '@/__tests__/utils';
|
||||
import Telemetry from './Telemetry.vue';
|
||||
import { useRootStore } from '@n8n/stores/useRootStore';
|
||||
import { useSettingsStore } from '@/app/stores/settings.store';
|
||||
import { useUsersStore } from '@/features/settings/users/users.store';
|
||||
import { useTelemetry } from '@/app/composables/useTelemetry';
|
||||
|
||||
vi.mock('vue-router', () => {
|
||||
const meta = {};
|
||||
return {
|
||||
useRouter: vi.fn(),
|
||||
useRoute: () => ({
|
||||
meta,
|
||||
}),
|
||||
RouterLink: {
|
||||
template: '<a><slot /></a>',
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('@/app/composables/useTelemetry', () => {
|
||||
const init = vi.fn();
|
||||
return {
|
||||
useTelemetry: () => ({
|
||||
init,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
const renderComponent = createComponentRenderer(Telemetry, {
|
||||
pinia: createTestingPinia(),
|
||||
});
|
||||
|
||||
let route: ReturnType<typeof useRoute>;
|
||||
let rootStore: MockedStore<typeof useRootStore>;
|
||||
let settingsStore: MockedStore<typeof useSettingsStore>;
|
||||
let usersStore: MockedStore<typeof useUsersStore>;
|
||||
let telemetryPlugin: ReturnType<typeof useTelemetry>;
|
||||
|
||||
describe('Telemetry', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
route = useRoute();
|
||||
rootStore = mockedStore(useRootStore);
|
||||
settingsStore = mockedStore(useSettingsStore);
|
||||
usersStore = mockedStore(useUsersStore);
|
||||
telemetryPlugin = useTelemetry();
|
||||
});
|
||||
|
||||
it('should not throw error when opened', async () => {
|
||||
expect(() => renderComponent()).not.toThrow();
|
||||
});
|
||||
|
||||
it('should initialize if telemetry is enabled in settings and not disabled on the route', async () => {
|
||||
settingsStore.telemetry = {
|
||||
enabled: true,
|
||||
};
|
||||
usersStore.currentUserId = '123';
|
||||
rootStore.instanceId = '456';
|
||||
renderComponent();
|
||||
|
||||
expect(telemetryPlugin.init).toHaveBeenCalledWith(
|
||||
{
|
||||
enabled: true,
|
||||
},
|
||||
expect.objectContaining({
|
||||
userId: '123',
|
||||
instanceId: '456',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should not initialize if telemetry is disabled in settings', async () => {
|
||||
settingsStore.telemetry = {
|
||||
enabled: false,
|
||||
};
|
||||
renderComponent();
|
||||
|
||||
expect(telemetryPlugin.init).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not initialize if telemetry is disabled on the route', async () => {
|
||||
settingsStore.telemetry = {
|
||||
enabled: true,
|
||||
};
|
||||
route.meta.telemetry = {
|
||||
disabled: true,
|
||||
};
|
||||
renderComponent();
|
||||
|
||||
expect(telemetryPlugin.init).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should render the iframe with correct src', async () => {
|
||||
settingsStore.telemetry = {
|
||||
enabled: true,
|
||||
};
|
||||
usersStore.currentUserId = '123';
|
||||
rootStore.instanceId = '456';
|
||||
const { container } = renderComponent();
|
||||
|
||||
const iframe = container.querySelector('iframe');
|
||||
|
||||
expect(iframe).toBeInTheDocument();
|
||||
expect(iframe).not.toBeVisible();
|
||||
expect(iframe).toHaveAttribute('src', expect.stringContaining('userId=123'));
|
||||
expect(iframe).toHaveAttribute('src', expect.stringContaining('instanceId=456'));
|
||||
});
|
||||
|
||||
it('should not render the iframe if telemetry disabled', async () => {
|
||||
settingsStore.telemetry = {
|
||||
enabled: false,
|
||||
};
|
||||
usersStore.currentUserId = '123';
|
||||
rootStore.instanceId = '456';
|
||||
const { container } = renderComponent();
|
||||
|
||||
const iframe = container.querySelector('iframe');
|
||||
|
||||
expect(iframe).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@ -1,73 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import type { ITelemetrySettings } from '@n8n/api-types';
|
||||
import { useRootStore } from '@n8n/stores/useRootStore';
|
||||
import { useSettingsStore } from '@/app/stores/settings.store';
|
||||
import { useUsersStore } from '@/features/settings/users/users.store';
|
||||
import { useProjectsStore } from '@/features/collaboration/projects/projects.store';
|
||||
import { computed, onMounted, watch, ref } from 'vue';
|
||||
import { useTelemetry } from '@/app/composables/useTelemetry';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
const isTelemetryInitialized = ref(false);
|
||||
|
||||
const rootStore = useRootStore();
|
||||
const settingsStore = useSettingsStore();
|
||||
const usersStore = useUsersStore();
|
||||
const projectsStore = useProjectsStore();
|
||||
const telemetryPlugin = useTelemetry();
|
||||
const route = useRoute();
|
||||
|
||||
const currentUserId = computed((): string => {
|
||||
return usersStore.currentUserId ?? '';
|
||||
});
|
||||
|
||||
const isTelemetryEnabledOnRoute = computed((): boolean => {
|
||||
const routeMeta = route.meta as { telemetry?: { disabled?: boolean } } | undefined;
|
||||
return routeMeta?.telemetry ? !routeMeta.telemetry.disabled : true;
|
||||
});
|
||||
|
||||
const telemetry = computed((): ITelemetrySettings => {
|
||||
return settingsStore.telemetry;
|
||||
});
|
||||
|
||||
const isTelemetryEnabled = computed((): boolean => {
|
||||
return !!telemetry.value?.enabled;
|
||||
});
|
||||
|
||||
const selfInstallSrc = computed((): string => {
|
||||
return `https://n8n.io/self-install?instanceId=${rootStore.instanceId}&userId=${currentUserId.value}`;
|
||||
});
|
||||
|
||||
watch(telemetry, () => {
|
||||
init();
|
||||
});
|
||||
|
||||
watch(isTelemetryEnabledOnRoute, (enabled) => {
|
||||
if (enabled) {
|
||||
init();
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
|
||||
function init() {
|
||||
if (isTelemetryInitialized.value || !isTelemetryEnabledOnRoute.value || !isTelemetryEnabled.value)
|
||||
return;
|
||||
|
||||
telemetryPlugin.init(telemetry.value, {
|
||||
instanceId: rootStore.instanceId,
|
||||
userId: currentUserId.value,
|
||||
projectId: projectsStore.personalProject?.id,
|
||||
versionCli: rootStore.versionCli,
|
||||
});
|
||||
|
||||
isTelemetryInitialized.value = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<iframe v-if="isTelemetryEnabled && currentUserId" v-show="false" :src="selfInstallSrc" />
|
||||
<span v-else v-show="false" />
|
||||
</template>
|
||||
@ -0,0 +1,140 @@
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { defineComponent, h, nextTick } from 'vue';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import { useTelemetryInitializer } from './useTelemetryInitializer';
|
||||
import { useTelemetry } from '@/app/composables/useTelemetry';
|
||||
import type { Project } from '@/features/collaboration/projects/projects.types';
|
||||
|
||||
const mockRouteMeta: Record<string, unknown> = {};
|
||||
|
||||
vi.mock('vue-router', () => ({
|
||||
useRouter: vi.fn(),
|
||||
useRoute: () => ({
|
||||
meta: mockRouteMeta,
|
||||
}),
|
||||
RouterLink: {
|
||||
template: '<a><slot /></a>',
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('@/app/composables/useTelemetry', () => {
|
||||
const init = vi.fn();
|
||||
return {
|
||||
useTelemetry: () => ({
|
||||
init,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
const TestComponent = defineComponent({
|
||||
setup() {
|
||||
useTelemetryInitializer();
|
||||
return () => h('div');
|
||||
},
|
||||
});
|
||||
|
||||
describe('useTelemetryInitializer', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
// Clear route meta
|
||||
Object.keys(mockRouteMeta).forEach((key) => delete mockRouteMeta[key]);
|
||||
});
|
||||
|
||||
it('should not throw error when called', () => {
|
||||
expect(() =>
|
||||
mount(TestComponent, { global: { plugins: [createTestingPinia()] } }),
|
||||
).not.toThrow();
|
||||
});
|
||||
|
||||
it('should initialize if telemetry is enabled in settings and not disabled on the route', async () => {
|
||||
const pinia = createTestingPinia({
|
||||
stubActions: false,
|
||||
initialState: {
|
||||
users: {
|
||||
currentUserId: '123',
|
||||
},
|
||||
projects: {
|
||||
personalProject: mock<Project>({ id: '789' }),
|
||||
},
|
||||
settings: {
|
||||
settings: {
|
||||
telemetry: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const telemetryPlugin = useTelemetry();
|
||||
const wrapper = mount(TestComponent, { global: { plugins: [pinia] } });
|
||||
|
||||
await nextTick();
|
||||
|
||||
expect(telemetryPlugin.init).toHaveBeenCalledWith(
|
||||
{
|
||||
enabled: true,
|
||||
},
|
||||
expect.objectContaining({
|
||||
userId: '123',
|
||||
projectId: '789',
|
||||
}),
|
||||
);
|
||||
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it('should not initialize if telemetry is disabled in settings', async () => {
|
||||
const pinia = createTestingPinia({
|
||||
stubActions: false,
|
||||
initialState: {
|
||||
settings: {
|
||||
settings: {
|
||||
telemetry: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const telemetryPlugin = useTelemetry();
|
||||
const wrapper = mount(TestComponent, { global: { plugins: [pinia] } });
|
||||
|
||||
await nextTick();
|
||||
|
||||
expect(telemetryPlugin.init).not.toHaveBeenCalled();
|
||||
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it('should not initialize if telemetry is disabled on the route', async () => {
|
||||
// Set route meta before mounting
|
||||
mockRouteMeta.telemetry = {
|
||||
disabled: true,
|
||||
};
|
||||
|
||||
const pinia = createTestingPinia({
|
||||
stubActions: false,
|
||||
initialState: {
|
||||
settings: {
|
||||
settings: {
|
||||
telemetry: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const telemetryPlugin = useTelemetry();
|
||||
const wrapper = mount(TestComponent, { global: { plugins: [pinia] } });
|
||||
|
||||
await nextTick();
|
||||
|
||||
expect(telemetryPlugin.init).not.toHaveBeenCalled();
|
||||
|
||||
wrapper.unmount();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,71 @@
|
||||
import type { ITelemetrySettings } from '@n8n/api-types';
|
||||
import { useRootStore } from '@n8n/stores/useRootStore';
|
||||
import { useSettingsStore } from '@/app/stores/settings.store';
|
||||
import { useUsersStore } from '@/features/settings/users/users.store';
|
||||
import { useProjectsStore } from '@/features/collaboration/projects/projects.store';
|
||||
import { computed, onMounted, watch, ref } from 'vue';
|
||||
import { useTelemetry } from '@/app/composables/useTelemetry';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
/**
|
||||
* Initializes the telemetry for the application
|
||||
*/
|
||||
export function useTelemetryInitializer() {
|
||||
const isTelemetryInitialized = ref(false);
|
||||
|
||||
const rootStore = useRootStore();
|
||||
const settingsStore = useSettingsStore();
|
||||
const usersStore = useUsersStore();
|
||||
const projectsStore = useProjectsStore();
|
||||
const telemetryPlugin = useTelemetry();
|
||||
const route = useRoute();
|
||||
|
||||
const currentUserId = computed((): string => {
|
||||
return usersStore.currentUserId ?? '';
|
||||
});
|
||||
|
||||
const isTelemetryEnabledOnRoute = computed((): boolean => {
|
||||
const routeMeta = route.meta as { telemetry?: { disabled?: boolean } } | undefined;
|
||||
return routeMeta?.telemetry ? !routeMeta.telemetry.disabled : true;
|
||||
});
|
||||
|
||||
const telemetry = computed((): ITelemetrySettings => {
|
||||
return settingsStore.telemetry;
|
||||
});
|
||||
|
||||
const isTelemetryEnabled = computed((): boolean => {
|
||||
return !!telemetry.value?.enabled;
|
||||
});
|
||||
|
||||
function init() {
|
||||
if (
|
||||
isTelemetryInitialized.value ||
|
||||
!isTelemetryEnabledOnRoute.value ||
|
||||
!isTelemetryEnabled.value
|
||||
)
|
||||
return;
|
||||
|
||||
telemetryPlugin.init(telemetry.value, {
|
||||
instanceId: rootStore.instanceId,
|
||||
userId: currentUserId.value,
|
||||
projectId: projectsStore.personalProject?.id,
|
||||
versionCli: rootStore.versionCli,
|
||||
});
|
||||
|
||||
isTelemetryInitialized.value = true;
|
||||
}
|
||||
|
||||
watch(telemetry, () => {
|
||||
init();
|
||||
});
|
||||
|
||||
watch(isTelemetryEnabledOnRoute, (enabled) => {
|
||||
if (enabled) {
|
||||
init();
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
}
|
||||
@ -1,71 +0,0 @@
|
||||
import { test, expect } from '../../fixtures/base';
|
||||
import type { TestRequirements } from '../../Types';
|
||||
|
||||
const telemetryDisabledRequirements: TestRequirements = {
|
||||
config: {
|
||||
settings: {
|
||||
telemetry: { enabled: false },
|
||||
},
|
||||
},
|
||||
storage: {
|
||||
'n8n-telemetry': JSON.stringify({ enabled: false }),
|
||||
},
|
||||
};
|
||||
|
||||
const telemetryEnabledRequirements: TestRequirements = {
|
||||
config: {
|
||||
settings: {
|
||||
telemetry: { enabled: true },
|
||||
instanceId: 'test-instance-id',
|
||||
},
|
||||
},
|
||||
storage: {
|
||||
'n8n-telemetry': JSON.stringify({ enabled: true }),
|
||||
'n8n-instance-id': 'test-instance-id',
|
||||
},
|
||||
intercepts: {
|
||||
iframeRequest: {
|
||||
url: 'https://n8n.io/self-install*',
|
||||
response: '<html><body>Test iframe content</body></html>',
|
||||
contentType: 'text/html',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
test.describe('n8n.io iframe', () => {
|
||||
test.describe('when telemetry is disabled', () => {
|
||||
test('should not load the iframe when visiting /home/workflows', async ({
|
||||
n8n,
|
||||
setupRequirements,
|
||||
}) => {
|
||||
await setupRequirements(telemetryDisabledRequirements);
|
||||
|
||||
await n8n.page.goto('/');
|
||||
await n8n.page.waitForLoadState();
|
||||
await expect(n8n.iframe.getIframe()).not.toBeAttached();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('when telemetry is enabled', () => {
|
||||
test('should load the iframe when visiting /home/workflows @auth:owner', async ({
|
||||
n8n,
|
||||
setupRequirements,
|
||||
}) => {
|
||||
await setupRequirements(telemetryEnabledRequirements);
|
||||
|
||||
// Get current user ID from the API
|
||||
const currentUser = await n8n.api.get('/rest/login');
|
||||
const testInstanceId = 'test-instance-id';
|
||||
const testUserId = currentUser.id;
|
||||
const iframeUrl = `https://n8n.io/self-install?instanceId=${testInstanceId}&userId=${testUserId}`;
|
||||
|
||||
await n8n.page.goto('/');
|
||||
await n8n.page.waitForLoadState();
|
||||
|
||||
const iframeElement = n8n.iframe.getIframeBySrc(iframeUrl);
|
||||
await expect(iframeElement).toBeAttached();
|
||||
|
||||
await expect(iframeElement).toHaveAttribute('src', iframeUrl);
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user