mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
refactor(editor): Move user login and logout side effects into hooks (no-changelog) (#16663)
This commit is contained in:
@@ -24,7 +24,11 @@ vi.mock('@/composables/useToast', () => ({
|
||||
}));
|
||||
|
||||
vi.mock('@/stores/users.store', () => ({
|
||||
useUsersStore: vi.fn().mockReturnValue({ initialize: vi.fn() }),
|
||||
useUsersStore: vi.fn().mockReturnValue({
|
||||
initialize: vi.fn(),
|
||||
registerLoginHook: vi.fn(),
|
||||
registerLogoutHook: vi.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('@n8n/stores/useRootStore', () => ({
|
||||
@@ -86,6 +90,16 @@ describe('Init', () => {
|
||||
expect(settingsStoreSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should initialize authentication hooks', async () => {
|
||||
const registerLoginHookSpy = vi.spyOn(usersStore, 'registerLoginHook');
|
||||
const registerLogoutHookSpy = vi.spyOn(usersStore, 'registerLogoutHook');
|
||||
|
||||
await initializeCore();
|
||||
|
||||
expect(registerLoginHookSpy).toHaveBeenCalled();
|
||||
expect(registerLogoutHookSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should initialize ssoStore with settings SSO configuration', async () => {
|
||||
const saml = { loginEnabled: true, loginLabel: '' };
|
||||
const ldap = { loginEnabled: false, loginLabel: '' };
|
||||
|
||||
@@ -18,6 +18,10 @@ import { EnterpriseEditionFeature } from '@/constants';
|
||||
import type { UserManagementAuthenticationMethod } from '@/Interface';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
import type { BannerName } from '@n8n/api-types';
|
||||
import { useNpsSurveyStore } from '@/stores/npsSurvey.store';
|
||||
import { usePostHog } from '@/stores/posthog.store';
|
||||
import { useTelemetry } from '@/composables/useTelemetry';
|
||||
import { useRBACStore } from '@/stores/rbac.store';
|
||||
|
||||
export const state = {
|
||||
initialized: false,
|
||||
@@ -39,6 +43,12 @@ export async function initializeCore() {
|
||||
const ssoStore = useSSOStore();
|
||||
const uiStore = useUIStore();
|
||||
|
||||
registerAuthenticationHooks();
|
||||
|
||||
/**
|
||||
* Initialize stores
|
||||
*/
|
||||
|
||||
await settingsStore.initialize();
|
||||
|
||||
ssoStore.initialize({
|
||||
@@ -146,3 +156,30 @@ export async function initializeAuthenticatedFeatures(
|
||||
|
||||
authenticatedFeaturesInitialized = true;
|
||||
}
|
||||
|
||||
function registerAuthenticationHooks() {
|
||||
const rootStore = useRootStore();
|
||||
const usersStore = useUsersStore();
|
||||
const cloudPlanStore = useCloudPlanStore();
|
||||
const postHogStore = usePostHog();
|
||||
const uiStore = useUIStore();
|
||||
const npsSurveyStore = useNpsSurveyStore();
|
||||
const telemetry = useTelemetry();
|
||||
const RBACStore = useRBACStore();
|
||||
|
||||
usersStore.registerLoginHook((user) => {
|
||||
RBACStore.setGlobalScopes(user.globalScopes ?? []);
|
||||
telemetry.identify(rootStore.instanceId, user.id);
|
||||
postHogStore.init(user.featureFlags);
|
||||
npsSurveyStore.setupNpsSurveyOnLogin(user.id, user.settings);
|
||||
});
|
||||
|
||||
usersStore.registerLogoutHook(() => {
|
||||
uiStore.clearBannerStack();
|
||||
npsSurveyStore.resetNpsSurveyOnLogOut();
|
||||
postHogStore.reset();
|
||||
cloudPlanStore.reset();
|
||||
telemetry.reset();
|
||||
RBACStore.setGlobalScopes([]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -44,7 +44,12 @@ function setCurrentUser() {
|
||||
|
||||
function resetStores() {
|
||||
useSettingsStore().reset();
|
||||
useUsersStore().reset();
|
||||
|
||||
const usersStore = useUsersStore();
|
||||
usersStore.initialized = false;
|
||||
usersStore.currentUserId = null;
|
||||
usersStore.usersById = {};
|
||||
usersStore.currentUserCloudInfo = null;
|
||||
}
|
||||
|
||||
function setup() {
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { CurrentUserResponse } from '@/Interface';
|
||||
import { useUsersStore } from './users.store';
|
||||
import { createPinia, setActivePinia } from 'pinia';
|
||||
|
||||
const { loginCurrentUser, identify, inviteUsers } = vi.hoisted(() => {
|
||||
const { loginCurrentUser, inviteUsers } = vi.hoisted(() => {
|
||||
return {
|
||||
loginCurrentUser: vi.fn(),
|
||||
identify: vi.fn(),
|
||||
@@ -18,12 +18,6 @@ vi.mock('@/api/invitation', () => ({
|
||||
inviteUsers,
|
||||
}));
|
||||
|
||||
vi.mock('@/composables/useTelemetry', () => ({
|
||||
useTelemetry: vi.fn(() => ({
|
||||
identify,
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock('@n8n/stores/useRootStore', () => ({
|
||||
useRootStore: vi.fn(() => ({
|
||||
instanceId: 'test-instance-id',
|
||||
@@ -59,8 +53,6 @@ describe('users.store', () => {
|
||||
isDefaultUser: false,
|
||||
isPendingUser: false,
|
||||
});
|
||||
|
||||
expect(identify).toHaveBeenCalledWith('test-instance-id', mockUser.id);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -22,17 +22,11 @@ import type { Cloud } from '@n8n/rest-api-client/api/cloudPlans';
|
||||
import { getPersonalizedNodeTypes } from '@/utils/userUtils';
|
||||
import { defineStore } from 'pinia';
|
||||
import { useRootStore } from '@n8n/stores/useRootStore';
|
||||
import { usePostHog } from './posthog.store';
|
||||
import { useUIStore } from './ui.store';
|
||||
import { useCloudPlanStore } from './cloudPlan.store';
|
||||
import * as mfaApi from '@n8n/rest-api-client/api/mfa';
|
||||
import * as cloudApi from '@n8n/rest-api-client/api/cloudPlans';
|
||||
import { useRBACStore } from '@/stores/rbac.store';
|
||||
import type { Scope } from '@n8n/permissions';
|
||||
import * as invitationsApi from '@/api/invitation';
|
||||
import { useNpsSurveyStore } from './npsSurvey.store';
|
||||
import { computed, ref } from 'vue';
|
||||
import { useTelemetry } from '@/composables/useTelemetry';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
import * as onboardingApi from '@/api/workflow-webhooks';
|
||||
import * as promptsApi from '@n8n/rest-api-client/api/prompts';
|
||||
@@ -42,6 +36,9 @@ const _isInstanceOwner = (user: IUserResponse | null) => user?.role === ROLE.Own
|
||||
const _isDefaultUser = (user: IUserResponse | null) =>
|
||||
_isInstanceOwner(user) && _isPendingUser(user);
|
||||
|
||||
type LoginHook = (user: CurrentUserResponse) => void;
|
||||
type LogoutHook = () => void;
|
||||
|
||||
export const useUsersStore = defineStore(STORES.USERS, () => {
|
||||
const initialized = ref(false);
|
||||
const currentUserId = ref<string | null>(null);
|
||||
@@ -49,20 +46,14 @@ export const useUsersStore = defineStore(STORES.USERS, () => {
|
||||
const currentUserCloudInfo = ref<Cloud.UserAccount | null>(null);
|
||||
const userQuota = ref<number>(-1);
|
||||
|
||||
const loginHooks = ref<LoginHook[]>([]);
|
||||
const logoutHooks = ref<LogoutHook[]>([]);
|
||||
|
||||
// Stores
|
||||
|
||||
const RBACStore = useRBACStore();
|
||||
const npsSurveyStore = useNpsSurveyStore();
|
||||
const uiStore = useUIStore();
|
||||
const rootStore = useRootStore();
|
||||
const settingsStore = useSettingsStore();
|
||||
const cloudPlanStore = useCloudPlanStore();
|
||||
|
||||
const telemetry = useTelemetry();
|
||||
|
||||
// Composables
|
||||
|
||||
const postHogStore = usePostHog();
|
||||
|
||||
// Computed
|
||||
|
||||
@@ -153,11 +144,13 @@ export const useUsersStore = defineStore(STORES.USERS, () => {
|
||||
addUsers([user]);
|
||||
currentUserId.value = user.id;
|
||||
|
||||
const defaultScopes: Scope[] = [];
|
||||
RBACStore.setGlobalScopes(user.globalScopes || defaultScopes);
|
||||
telemetry.identify(rootStore.instanceId, user.id);
|
||||
postHogStore.init(user.featureFlags);
|
||||
npsSurveyStore.setupNpsSurveyOnLogin(user.id, user.settings);
|
||||
for (const hook of loginHooks.value) {
|
||||
try {
|
||||
hook(user);
|
||||
} catch (error) {
|
||||
console.error('Error executing login hook:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const loginWithCookie = async () => {
|
||||
@@ -187,8 +180,6 @@ export const useUsersStore = defineStore(STORES.USERS, () => {
|
||||
const unsetCurrentUser = () => {
|
||||
currentUserId.value = null;
|
||||
currentUserCloudInfo.value = null;
|
||||
telemetry.reset();
|
||||
RBACStore.setGlobalScopes([]);
|
||||
};
|
||||
|
||||
const deleteUserById = (userId: string) => {
|
||||
@@ -219,13 +210,26 @@ export const useUsersStore = defineStore(STORES.USERS, () => {
|
||||
setCurrentUser(user);
|
||||
};
|
||||
|
||||
const registerLoginHook = (hook: LoginHook) => {
|
||||
loginHooks.value.push(hook);
|
||||
};
|
||||
|
||||
const registerLogoutHook = (hook: LogoutHook) => {
|
||||
logoutHooks.value.push(hook);
|
||||
};
|
||||
|
||||
const logout = async () => {
|
||||
await usersApi.logout(rootStore.restApiContext);
|
||||
|
||||
unsetCurrentUser();
|
||||
cloudPlanStore.reset();
|
||||
postHogStore.reset();
|
||||
uiStore.clearBannerStack();
|
||||
npsSurveyStore.resetNpsSurveyOnLogOut();
|
||||
|
||||
for (const hook of logoutHooks.value) {
|
||||
try {
|
||||
hook();
|
||||
} catch (error) {
|
||||
console.error('Error executing logout hook:', error);
|
||||
}
|
||||
}
|
||||
|
||||
localStorage.removeItem(BROWSER_ID_STORAGE_KEY);
|
||||
};
|
||||
@@ -406,13 +410,6 @@ export const useUsersStore = defineStore(STORES.USERS, () => {
|
||||
await fetchUsers();
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
initialized.value = false;
|
||||
currentUserId.value = null;
|
||||
usersById.value = {};
|
||||
currentUserCloudInfo.value = null;
|
||||
};
|
||||
|
||||
const submitContactEmail = async (email: string, agree: boolean) => {
|
||||
if (currentUser.value) {
|
||||
return await onboardingApi.submitEmailOnSignup(
|
||||
@@ -459,6 +456,8 @@ export const useUsersStore = defineStore(STORES.USERS, () => {
|
||||
setPersonalizationAnswers,
|
||||
loginWithCreds,
|
||||
logout,
|
||||
registerLoginHook,
|
||||
registerLogoutHook,
|
||||
createOwner,
|
||||
validateSignupToken,
|
||||
acceptInvitation,
|
||||
@@ -485,7 +484,6 @@ export const useUsersStore = defineStore(STORES.USERS, () => {
|
||||
fetchUserCloudAccount,
|
||||
sendConfirmationEmail,
|
||||
updateGlobalRole,
|
||||
reset,
|
||||
setEasyAIWorkflowOnboardingDone,
|
||||
isCalloutDismissed,
|
||||
setCalloutDismissed,
|
||||
|
||||
Reference in New Issue
Block a user