refactor: Add common DTOs and schemas for the redesigned users list (#16097)

This commit is contained in:
Csaba Tuncsik
2025-06-11 14:55:04 +02:00
committed by GitHub
parent b9e03515bd
commit e681e6b477
23 changed files with 348 additions and 52 deletions

View File

@@ -6,6 +6,8 @@ import type {
Iso8601DateTimeString,
IUserManagementSettings,
IVersionNotificationSettings,
ROLE,
Role,
} from '@n8n/api-types';
import type { Scope } from '@n8n/permissions';
import type { NodeCreatorTag } from '@n8n/design-system';
@@ -55,7 +57,6 @@ import type {
TRIGGER_NODE_CREATOR_VIEW,
REGULAR_NODE_CREATOR_VIEW,
AI_OTHERS_NODE_CREATOR_VIEW,
ROLE,
AI_UNCATEGORIZED_CATEGORY,
AI_EVALUATION,
} from '@/constants';
@@ -568,9 +569,7 @@ export type IPersonalizationSurveyVersions =
| IPersonalizationSurveyAnswersV3
| IPersonalizationSurveyAnswersV4;
export type Roles = typeof ROLE;
export type IRole = Roles[keyof Roles];
export type InvitableRoleName = Roles['Member' | 'Admin'];
export type InvitableRoleName = (typeof ROLE)['Member' | 'Admin'];
export interface IUserResponse {
id: string;
@@ -578,7 +577,7 @@ export interface IUserResponse {
lastName?: string;
email?: string;
createdAt?: string;
role?: IRole;
role?: Role;
globalScopes?: Scope[];
personalizationAnswers?: IPersonalizationSurveyVersions | null;
isPending: boolean;
@@ -613,7 +612,7 @@ export const enum UserManagementAuthenticationMethod {
export interface IPermissionGroup {
loginStatus?: ILogInStatus[];
role?: IRole[];
role?: Role[];
}
export interface IPermissionAllowGroup extends IPermissionGroup {
@@ -1168,7 +1167,7 @@ export interface IInviteResponse {
email: string;
emailSent: boolean;
inviteAcceptUrl: string;
role: IRole;
role: Role;
};
error?: string;
}

View File

@@ -2,7 +2,7 @@ import merge from 'lodash/merge';
import userEvent from '@testing-library/user-event';
import { SETTINGS_STORE_DEFAULT_STATE } from '@/__tests__/utils';
import { ROLE } from '@/constants';
import { ROLE } from '@n8n/api-types';
import { STORES } from '@n8n/stores';
import { createTestingPinia } from '@pinia/testing';

View File

@@ -3,12 +3,8 @@ import { computed, onMounted, ref } from 'vue';
import { useToast } from '@/composables/useToast';
import Modal from './Modal.vue';
import type { IFormInputs, IInviteResponse, IUser, InvitableRoleName } from '@/Interface';
import {
EnterpriseEditionFeature,
VALID_EMAIL_REGEX,
INVITE_USER_MODAL_KEY,
ROLE,
} from '@/constants';
import { EnterpriseEditionFeature, VALID_EMAIL_REGEX, INVITE_USER_MODAL_KEY } from '@/constants';
import { ROLE } from '@n8n/api-types';
import { useUsersStore } from '@/stores/users.store';
import { useSettingsStore } from '@/stores/settings.store';
import { createFormEventBus } from '@n8n/design-system/utils';

View File

@@ -2,7 +2,7 @@ import { createComponentRenderer } from '@/__tests__/render';
import V1Banner from './V1Banner.vue';
import { createPinia, setActivePinia } from 'pinia';
import { useUsersStore } from '@/stores/users.store';
import { ROLE } from '@/constants';
import { ROLE } from '@n8n/api-types';
import type { IUser } from '@/Interface';
const renderComponent = createComponentRenderer(V1Banner);

View File

@@ -1,4 +1,4 @@
import { ROLE } from '@/constants';
import { ROLE } from '@n8n/api-types';
import { useSettingsStore } from '@/stores/settings.store';
import merge from 'lodash/merge';
import { usePageRedirectionHelper } from './usePageRedirectionHelper';

View File

@@ -879,13 +879,6 @@ export const TEMPLATES_URLS = {
},
};
export const ROLE = {
Owner: 'global:owner',
Member: 'global:member',
Admin: 'global:admin',
Default: 'default', // default user with no email when setting up instance
} as const;
export const INSECURE_CONNECTION_WARNING = `
<body style="margin-top: 20px; font-family: 'Open Sans', sans-serif; text-align: center;">
<h1 style="font-size: 40px">&#x1F6AB;</h1>

View File

@@ -3,10 +3,10 @@ import { hasScope as genericHasScope } from '@n8n/permissions';
import type { ScopeOptions, Scope, Resource } from '@n8n/permissions';
import { ref } from 'vue';
import { STORES } from '@n8n/stores';
import type { IRole } from '@/Interface';
import type { Role } from '@n8n/api-types';
export const useRBACStore = defineStore(STORES.RBAC, () => {
const globalRoles = ref<IRole[]>([]);
const globalRoles = ref<Role[]>([]);
const rolesByProjectId = ref<Record<string, string[]>>({});
const globalScopes = ref<Scope[]>([]);
@@ -38,13 +38,13 @@ export const useRBACStore = defineStore(STORES.RBAC, () => {
insights: {},
});
function addGlobalRole(role: IRole) {
function addGlobalRole(role: Role) {
if (!globalRoles.value.includes(role)) {
globalRoles.value.push(role);
}
}
function hasRole(role: IRole) {
function hasRole(role: Role) {
return globalRoles.value.includes(role);
}

View File

@@ -12,14 +12,13 @@ import {
getUserCloudInfo,
getNotTrialingUserResponse,
} from './__tests__/utils/cloudStoreUtils';
import type { IRole } from '@/Interface';
import { ROLE } from '@/constants';
import { ROLE, type Role } from '@n8n/api-types';
let uiStore: ReturnType<typeof useUIStore>;
let settingsStore: ReturnType<typeof useSettingsStore>;
let cloudPlanStore: ReturnType<typeof useCloudPlanStore>;
function setUser(role: IRole) {
function setUser(role: Role) {
useUsersStore().addUsers([
{
id: '1',

View File

@@ -1,13 +1,14 @@
import type {
LoginRequestDto,
PasswordUpdateRequestDto,
SettingsUpdateRequestDto,
UserUpdateRequestDto,
import {
type LoginRequestDto,
type PasswordUpdateRequestDto,
type SettingsUpdateRequestDto,
type UserUpdateRequestDto,
ROLE,
} from '@n8n/api-types';
import type { UpdateGlobalRolePayload } from '@/api/users';
import * as usersApi from '@/api/users';
import { BROWSER_ID_STORAGE_KEY } from '@n8n/constants';
import { PERSONALIZATION_MODAL_KEY, ROLE } from '@/constants';
import { PERSONALIZATION_MODAL_KEY } from '@/constants';
import { STORES } from '@n8n/stores';
import type {
IPersonalizationLatestVersion,

View File

@@ -1,5 +1,6 @@
import type { Resource, ScopeOptions, Scope } from '@n8n/permissions';
import type { EnterpriseEditionFeatureValue, IRole } from '@/Interface';
import type { EnterpriseEditionFeatureValue } from '@/Interface';
import type { Role } from '@n8n/api-types';
export type AuthenticatedPermissionOptions = {
bypass?: () => boolean;
@@ -19,7 +20,7 @@ export type RBACPermissionOptions = {
resourceId?: string;
options?: ScopeOptions;
};
export type RolePermissionOptions = IRole[];
export type RolePermissionOptions = Role[];
export type PermissionType =
| 'authenticated'

View File

@@ -1,6 +1,6 @@
import { useUsersStore } from '@/stores/users.store';
import { hasRole } from '@/utils/rbac/checks';
import { ROLE } from '@/constants';
import { ROLE } from '@n8n/api-types';
vi.mock('@/stores/users.store', () => ({
useUsersStore: vi.fn(),

View File

@@ -1,7 +1,6 @@
import { useUsersStore } from '@/stores/users.store';
import type { RBACPermissionCheck, RolePermissionOptions } from '@/types/rbac';
import { ROLE } from '@/constants';
import type { IRole } from '@/Interface';
import { ROLE, type Role } from '@n8n/api-types';
export const hasRole: RBACPermissionCheck<RolePermissionOptions> = (checkRoles) => {
const usersStore = useUsersStore();
@@ -9,7 +8,7 @@ export const hasRole: RBACPermissionCheck<RolePermissionOptions> = (checkRoles)
if (currentUser && checkRoles) {
const userRole = currentUser.isDefaultUser ? ROLE.Default : currentUser.role;
return checkRoles.includes(userRole as IRole);
return checkRoles.includes(userRole as Role);
}
return false;

View File

@@ -2,7 +2,8 @@ import { roleMiddleware } from '@/utils/rbac/middleware/role';
import { useUsersStore } from '@/stores/users.store';
import type { IUser } from '@/Interface';
import type { RouteLocationNormalized } from 'vue-router';
import { VIEWS, ROLE } from '@/constants';
import { ROLE } from '@n8n/api-types';
import { VIEWS } from '@/constants';
vi.mock('@/stores/users.store', () => ({
useUsersStore: vi.fn(),

View File

@@ -59,8 +59,8 @@ import {
BAMBOO_HR_NODE_TYPE,
GOOGLE_SHEETS_NODE_TYPE,
CODE_NODE_TYPE,
ROLE,
} from '@/constants';
import { ROLE } from '@n8n/api-types';
import type {
IPersonalizationSurveyAnswersV1,
IPersonalizationSurveyAnswersV2,

View File

@@ -6,7 +6,7 @@ import { useSettingsStore } from '@/stores/settings.store';
import { useUsersStore } from '@/stores/users.store';
import { createComponentRenderer } from '@/__tests__/render';
import { setupServer } from '@/__tests__/server';
import { ROLE } from '@/constants';
import { ROLE } from '@n8n/api-types';
import { useUIStore } from '@/stores/ui.store';
import { useCloudPlanStore } from '@/stores/cloudPlan.store';

View File

@@ -1,15 +1,15 @@
<script lang="ts" setup>
import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
import { ROLE, type Role } from '@n8n/api-types';
import { useI18n } from '@n8n/i18n';
import { useToast } from '@/composables/useToast';
import { useDocumentTitle } from '@/composables/useDocumentTitle';
import type { IFormInputs, IRole, IUser, ThemeOption } from '@/Interface';
import type { IFormInputs, IUser, ThemeOption } from '@/Interface';
import {
CHANGE_PASSWORD_MODAL_KEY,
MFA_DOCS_URL,
MFA_SETUP_MODAL_KEY,
PROMPT_MFA_CODE_MODAL_KEY,
ROLE,
} from '@/constants';
import { useUIStore } from '@/stores/ui.store';
import { useUsersStore } from '@/stores/users.store';
@@ -90,7 +90,7 @@ const hasAnyChanges = computed(() => {
return hasAnyBasicInfoChanges.value || hasAnyPersonalisationChanges.value;
});
const roles = computed<Record<IRole, RoleContent>>(() => ({
const roles = computed<Record<Role, RoleContent>>(() => ({
[ROLE.Default]: {
name: i18n.baseText('auth.roles.default'),
description: i18n.baseText('settings.personal.role.tooltip.default'),

View File

@@ -1,7 +1,7 @@
<script lang="ts" setup>
import { EnterpriseEditionFeature, INVITE_USER_MODAL_KEY, ROLE } from '@/constants';
import type { IRole, IUser, IUserListAction, InvitableRoleName } from '@/Interface';
import { ROLE, type Role } from '@n8n/api-types';
import { EnterpriseEditionFeature, INVITE_USER_MODAL_KEY } from '@/constants';
import type { IUser, IUserListAction, InvitableRoleName } from '@/Interface';
import { useToast } from '@/composables/useToast';
import { useUIStore } from '@/stores/ui.store';
import { useSettingsStore } from '@/stores/settings.store';
@@ -85,7 +85,7 @@ const isAdvancedPermissionsEnabled = computed((): boolean => {
return settingsStore.isEnterpriseFeatureEnabled[EnterpriseEditionFeature.AdvancedPermissions];
});
const userRoles = computed((): Array<{ value: IRole; label: string; disabled?: boolean }> => {
const userRoles = computed((): Array<{ value: Role; label: string; disabled?: boolean }> => {
return [
{
value: ROLE.Member,