refactor(core): Move more code into @n8n/permissions. Add aditional tests and docs (no-changelog) (#15062)

Co-authored-by: Danny Martini <danny@n8n.io>
This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™
2025-05-06 15:11:05 +02:00
committed by GitHub
parent cdcd059248
commit 2bb190349b
85 changed files with 1011 additions and 775 deletions

View File

@@ -1,7 +1,7 @@
import type { AllRolesMap } from '@n8n/permissions';
import type { IRestApiContext } from '@/Interface';
import type { RoleMap } from '@/types/roles.types';
import { makeRestApiRequest } from '@/utils/apiUtils';
export const getRoles = async (context: IRestApiContext): Promise<RoleMap> => {
export const getRoles = async (context: IRestApiContext): Promise<AllRolesMap> => {
return await makeRestApiRequest(context, 'GET', '/roles');
};

View File

@@ -1,4 +1,5 @@
<script setup lang="ts">
import type { AllRolesMap } from '@n8n/permissions';
import ProjectSharing from '@/components/Projects/ProjectSharing.vue';
import { useI18n } from '@/composables/useI18n';
import { usePageRedirectionHelper } from '@/composables/usePageRedirectionHelper';
@@ -12,7 +13,6 @@ import { useUIStore } from '@/stores/ui.store';
import { useUsersStore } from '@/stores/users.store';
import type { ProjectListItem, ProjectSharingData } from '@/types/projects.types';
import { ProjectTypes } from '@/types/projects.types';
import type { RoleMap } from '@/types/roles.types';
import { splitName } from '@/utils/projects.utils';
import type { EventBus } from '@n8n/utils/event-bus';
import type { ICredentialDataDecryptedObject } from 'n8n-workflow';
@@ -82,7 +82,7 @@ const credentialRoleTranslations = computed<Record<string, string>>(() => {
};
});
const credentialRoles = computed<RoleMap['credential']>(() => {
const credentialRoles = computed<AllRolesMap['credential']>(() => {
return rolesStore.processedCredentialRoles.map(({ role, scopes, licensed }) => ({
role,
name: credentialRoleTranslations.value[role],

View File

@@ -1,9 +1,9 @@
<script lang="ts" setup>
import type { AllRolesMap } from '@n8n/permissions';
import { computed, ref, watch } from 'vue';
import { useI18n } from '@/composables/useI18n';
import type { ProjectListItem, ProjectSharingData } from '@/types/projects.types';
import ProjectSharingInfo from '@/components/Projects/ProjectSharingInfo.vue';
import type { RoleMap } from '@/types/roles.types';
import { sortByProperty } from '@n8n/utils/sort/sortByProperty';
const locale = useI18n();
@@ -11,7 +11,7 @@ const locale = useI18n();
type Props = {
projects: ProjectListItem[];
homeProject?: ProjectSharingData;
roles?: RoleMap['workflow' | 'credential' | 'project'];
roles?: AllRolesMap['workflow' | 'credential' | 'project'];
readonly?: boolean;
static?: boolean;
placeholder?: string;

View File

@@ -1,4 +1,4 @@
import type { ProjectRole, RoleMap } from '@/types/roles.types';
import type { ProjectRole, AllRolesMap } from '@n8n/permissions';
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
import * as rolesApi from '@/api/roles.api';
@@ -7,7 +7,7 @@ import { useRootStore } from './root.store';
export const useRolesStore = defineStore('roles', () => {
const rootStore = useRootStore();
const roles = ref<RoleMap>({
const roles = ref<AllRolesMap>({
global: [],
project: [],
credential: [],
@@ -22,7 +22,7 @@ export const useRolesStore = defineStore('roles', () => {
() => new Map(projectRoleOrder.value.map((role, idx) => [role, idx])),
);
const processedProjectRoles = computed<RoleMap['project']>(() =>
const processedProjectRoles = computed<AllRolesMap['project']>(() =>
roles.value.project
.filter((role) => projectRoleOrderMap.value.has(role.role))
.sort(
@@ -32,11 +32,11 @@ export const useRolesStore = defineStore('roles', () => {
),
);
const processedCredentialRoles = computed<RoleMap['credential']>(() =>
const processedCredentialRoles = computed<AllRolesMap['credential']>(() =>
roles.value.credential.filter((role) => role.role !== 'credential:owner'),
);
const processedWorkflowRoles = computed<RoleMap['workflow']>(() =>
const processedWorkflowRoles = computed<AllRolesMap['workflow']>(() =>
roles.value.workflow.filter((role) => role.role !== 'workflow:owner'),
);

View File

@@ -1,6 +1,5 @@
import type { Scope } from '@n8n/permissions';
import type { Scope, ProjectRole } from '@n8n/permissions';
import type { IUserResponse } from '@/Interface';
import type { ProjectRole } from '@/types/roles.types';
export const ProjectTypes = {
Personal: 'personal',

View File

@@ -1,25 +0,0 @@
import type { Scope } from '@n8n/permissions';
export type GlobalRole = 'global:owner' | 'global:admin' | 'global:member';
export type ProjectRole =
| 'project:personalOwner'
| 'project:admin'
| 'project:editor'
| 'project:viewer';
export type CredentialSharingRole = 'credential:owner' | 'credential:user';
export type WorkflowSharingRole = 'workflow:owner' | 'workflow:editor';
export type RoleObject<
T extends GlobalRole | ProjectRole | CredentialSharingRole | WorkflowSharingRole,
> = {
role: T;
name: string;
scopes: Scope[];
licensed: boolean;
};
export type RoleMap = {
global: Array<RoleObject<GlobalRole>>;
project: Array<RoleObject<ProjectRole>>;
credential: Array<RoleObject<CredentialSharingRole>>;
workflow: Array<RoleObject<WorkflowSharingRole>>;
};

View File

@@ -1,4 +1,5 @@
<script lang="ts" setup>
import type { ProjectRole } from '@n8n/permissions';
import { computed, ref, watch, onBeforeMount, onMounted, nextTick } from 'vue';
import { useRouter } from 'vue-router';
import { deepCopy } from 'n8n-workflow';
@@ -13,7 +14,6 @@ import { VIEWS } from '@/constants';
import ProjectDeleteDialog from '@/components/Projects/ProjectDeleteDialog.vue';
import ProjectRoleUpgradeDialog from '@/components/Projects/ProjectRoleUpgradeDialog.vue';
import { useRolesStore } from '@/stores/roles.store';
import type { ProjectRole } from '@/types/roles.types';
import { useCloudPlanStore } from '@/stores/cloudPlan.store';
import { useTelemetry } from '@/composables/useTelemetry';
import { useDocumentTitle } from '@/composables/useDocumentTitle';