refactor(core): Decouple module settings from frontend service (#16324)

Co-authored-by: Danny Martini <danny@n8n.io>
This commit is contained in:
Iván Ovejero
2025-06-18 10:00:02 +02:00
committed by GitHub
parent 49b9439ec0
commit 6ba8e0bebe
28 changed files with 227 additions and 172 deletions

View File

@@ -145,21 +145,8 @@ export const defaultSettings: FrontendSettings = {
folders: {
enabled: false,
},
insights: {
enabled: false,
summary: true,
dashboard: false,
dateRanges: [
{ key: 'day', licensed: true, granularity: 'hour' },
{ key: 'week', licensed: true, granularity: 'day' },
{ key: '2weeks', licensed: true, granularity: 'day' },
{ key: 'month', licensed: false, granularity: 'day' },
{ key: 'quarter', licensed: false, granularity: 'week' },
{ key: '6months', licensed: false, granularity: 'week' },
{ key: 'year', licensed: false, granularity: 'week' },
],
},
evaluation: {
quota: 0,
},
loadedModules: [],
};

View File

@@ -112,7 +112,7 @@ const mainMenuItems = computed(() => [
position: 'bottom',
route: { to: { name: VIEWS.INSIGHTS } },
available:
settingsStore.settings.insights.enabled &&
settingsStore.settings.loadedModules.includes('insights') &&
hasPermission(['rbac'], { rbac: { scope: 'insights:list' } }),
},
{

View File

@@ -13,9 +13,25 @@ const renderComponent = createComponentRenderer(InsightsDashboard, {
},
});
const moduleSettings = {
insights: {
summary: true,
dashboard: true,
dateRanges: [
{
key: 'day',
licensed: true,
granularity: 'hour',
},
],
},
};
describe('InsightsDashboard', () => {
beforeEach(() => {
createTestingPinia({ initialState: { settings: { settings: defaultSettings } } });
createTestingPinia({
initialState: { settings: { settings: defaultSettings, moduleSettings } },
});
});
it('should render without error', () => {

View File

@@ -18,8 +18,13 @@ export const useInsightsStore = defineStore('insights', () => {
() => getResourcePermissions(usersStore.currentUser?.globalScopes).insights,
);
const isInsightsEnabled = computed(() => settingsStore.settings.insights.enabled);
const isDashboardEnabled = computed(() => settingsStore.settings.insights.dashboard);
const isInsightsEnabled = computed(() =>
settingsStore.settings.loadedModules.includes('insights'),
);
const isDashboardEnabled = computed(
() => settingsStore.moduleSettings.insights?.dashboard ?? false,
);
const isSummaryEnabled = computed(
() => globalInsightsPermissions.value.list && isInsightsEnabled.value,
@@ -64,7 +69,7 @@ export const useInsightsStore = defineStore('insights', () => {
{ immediate: false, resetOnExecute: false },
);
const dateRanges = computed(() => settingsStore.settings.insights.dateRanges);
const dateRanges = computed(() => settingsStore.moduleSettings.insights?.dateRanges ?? []);
return {
globalInsightsPermissions,

View File

@@ -1,9 +1,14 @@
import { computed, ref } from 'vue';
import Bowser from 'bowser';
import type { IUserManagementSettings, FrontendSettings } from '@n8n/api-types';
import type {
IUserManagementSettings,
FrontendSettings,
FrontendModuleSettings,
} from '@n8n/api-types';
import * as eventsApi from '@n8n/rest-api-client/api/events';
import * as settingsApi from '@n8n/rest-api-client/api/settings';
import * as moduleSettingsApi from '@n8n/rest-api-client/api/module-settings';
import * as promptsApi from '@n8n/rest-api-client/api/prompts';
import { testHealthEndpoint } from '@/api/templates';
import {
@@ -26,6 +31,7 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => {
const i18n = useI18n();
const initialized = ref(false);
const settings = ref<FrontendSettings>({} as FrontendSettings);
const moduleSettings = ref<FrontendModuleSettings>({});
const userManagement = ref<IUserManagementSettings>({
quota: -1,
showSetupOnFirstLoad: false,
@@ -178,6 +184,8 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => {
const isDevRelease = computed(() => settings.value.releaseChannel === 'dev');
const loadedModules = computed(() => settings.value.loadedModules);
const setSettings = (newSettings: FrontendSettings) => {
settings.value = newSettings;
userManagement.value = newSettings.userManagement;
@@ -325,6 +333,11 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => {
settings.value = {} as FrontendSettings;
};
const getModuleSettings = async () => {
const fetched = await moduleSettingsApi.getModuleSettings(useRootStore().restApiContext);
moduleSettings.value = fetched;
};
/**
* (Experimental) Minimum zoom level of the canvas to render node settings in place of nodes, without opening NDV
*/
@@ -402,5 +415,8 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => {
getSettings,
setSettings,
initialize,
loadedModules,
getModuleSettings,
moduleSettings,
};
});

View File

@@ -46,6 +46,7 @@ let telemetry: ReturnType<typeof useTelemetry>;
describe('SigninView', () => {
const signInWithValidUser = async () => {
settingsStore.isCloudDeployment = false;
settingsStore.loadedModules = [];
usersStore.loginWithCreds.mockResolvedValueOnce();
const { getByRole, queryByTestId, container } = renderComponent();

View File

@@ -144,6 +144,11 @@ const login = async (form: LoginRequestDto) => {
}
}
await settingsStore.getSettings();
if (settingsStore.loadedModules.length > 0) {
await settingsStore.getModuleSettings();
}
toast.clearAllStickyNotifications();
telemetry.track('User attempted to login', {