import { defineStore } from 'pinia'; import { ref, watch, computed } from 'vue'; import { useRoute } from 'vue-router'; import { useRootStore } from '@/stores/root.store'; import * as projectsApi from '@/api/projects.api'; import * as workflowsEEApi from '@/api/workflows.ee'; import * as credentialsEEApi from '@/api/credentials.ee'; import type { Project, ProjectListItem, ProjectsCount } from '@/types/projects.types'; import { ProjectTypes } from '@/types/projects.types'; import { useSettingsStore } from '@/stores/settings.store'; import { hasPermission } from '@/utils/rbac/permissions'; import type { IWorkflowDb } from '@/Interface'; import { useCredentialsStore } from '@/stores/credentials.store'; import { STORES } from '@/constants'; import { useUsersStore } from '@/stores/users.store'; import { getResourcePermissions } from '@/permissions'; import type { CreateProjectDto, UpdateProjectDto } from '@n8n/api-types'; export const useProjectsStore = defineStore(STORES.PROJECTS, () => { const route = useRoute(); const rootStore = useRootStore(); const settingsStore = useSettingsStore(); const credentialsStore = useCredentialsStore(); const usersStore = useUsersStore(); const projects = ref([]); const myProjects = ref([]); const personalProject = ref(null); const currentProject = ref(null); const projectsCount = ref({ personal: 0, team: 0, public: 0, }); const projectNavActiveIdState = ref(null); const globalProjectPermissions = computed( () => getResourcePermissions(usersStore.currentUser?.globalScopes).project, ); const availableProjects = computed(() => globalProjectPermissions.value.list ? projects.value : myProjects.value, ); const currentProjectId = computed( () => (route.params?.projectId as string | undefined) ?? (route.query?.projectId as string | undefined) ?? currentProject.value?.id, ); const isProjectHome = computed(() => route.path.includes('home')); const personalProjects = computed(() => projects.value.filter((p) => p.type === ProjectTypes.Personal), ); const teamProjects = computed(() => projects.value.filter((p) => p.type === ProjectTypes.Team)); const teamProjectsLimit = computed(() => settingsStore.settings.enterprise.projects.team.limit); const isTeamProjectFeatureEnabled = computed(() => teamProjectsLimit.value !== 0); const hasUnlimitedProjects = computed(() => teamProjectsLimit.value === -1); const isTeamProjectLimitExceeded = computed( () => projectsCount.value.team >= teamProjectsLimit.value, ); const canCreateProjects = computed( () => hasUnlimitedProjects.value || (isTeamProjectFeatureEnabled.value && !isTeamProjectLimitExceeded.value), ); const hasPermissionToCreateProjects = computed(() => hasPermission(['rbac'], { rbac: { scope: 'project:create' } }), ); const projectNavActiveId = computed({ get: () => route?.params?.projectId ?? projectNavActiveIdState.value, set: (value: string | string[] | null) => { projectNavActiveIdState.value = value; }, }); const setCurrentProject = (project: Project | null) => { currentProject.value = project; }; const getAllProjects = async () => { projects.value = await projectsApi.getAllProjects(rootStore.restApiContext); }; const getMyProjects = async () => { myProjects.value = await projectsApi.getMyProjects(rootStore.restApiContext); }; const getPersonalProject = async () => { personalProject.value = await projectsApi.getPersonalProject(rootStore.restApiContext); }; const getAvailableProjects = async () => { if (globalProjectPermissions.value.list) { await getAllProjects(); } else { await getMyProjects(); } }; const fetchProject = async (id: string) => await projectsApi.getProject(rootStore.restApiContext, id); const getProject = async (id: string) => { currentProject.value = await fetchProject(id); }; const createProject = async (project: CreateProjectDto): Promise => { const newProject = await projectsApi.createProject(rootStore.restApiContext, project); await getProjectsCount(); myProjects.value = [...myProjects.value, newProject as unknown as ProjectListItem]; return newProject; }; const updateProject = async ( id: Project['id'], projectData: Required, ): Promise => { await projectsApi.updateProject(rootStore.restApiContext, id, projectData); const projectIndex = myProjects.value.findIndex((p) => p.id === id); const { name, icon } = projectData; if (projectIndex !== -1) { myProjects.value[projectIndex].name = name; myProjects.value[projectIndex].icon = icon; } if (currentProject.value) { currentProject.value.name = name; currentProject.value.icon = icon; } if (projectData.relations) { await getProject(id); } }; const deleteProject = async (projectId: string, transferId?: string): Promise => { await projectsApi.deleteProject(rootStore.restApiContext, projectId, transferId); await getProjectsCount(); myProjects.value = myProjects.value.filter((p) => p.id !== projectId); }; const getProjectsCount = async () => { projectsCount.value = await projectsApi.getProjectsCount(rootStore.restApiContext); }; const setProjectNavActiveIdByWorkflowHomeProject = async ( homeProject?: IWorkflowDb['homeProject'], ) => { if (homeProject?.type === ProjectTypes.Personal) { projectNavActiveId.value = 'home'; } else { projectNavActiveId.value = homeProject?.id ?? null; if (homeProject?.id && !currentProjectId.value) { await getProject(homeProject?.id); } } }; const moveResourceToProject = async ( resourceType: 'workflow' | 'credential', resourceId: string, projectId: string, shareCredentials?: string[], ) => { if (resourceType === 'workflow') { await workflowsEEApi.moveWorkflowToProject(rootStore.restApiContext, resourceId, { destinationProjectId: projectId, shareCredentials, }); } else { await credentialsEEApi.moveCredentialToProject( rootStore.restApiContext, resourceId, projectId, ); await credentialsStore.fetchAllCredentials(currentProjectId.value); } }; watch( route, async (newRoute) => { projectNavActiveId.value = null; if (newRoute?.path?.includes('home')) { projectNavActiveId.value = 'home'; setCurrentProject(null); } if (newRoute?.path?.includes('workflow/')) { if (currentProjectId.value) { projectNavActiveId.value = currentProjectId.value; } else { projectNavActiveId.value = 'home'; } } if (!newRoute?.params?.projectId) { return; } await getProject(newRoute.params.projectId as string); }, { immediate: true }, ); return { projects, availableProjects, myProjects, personalProject, currentProject, currentProjectId, isProjectHome, personalProjects, teamProjects, teamProjectsLimit, hasUnlimitedProjects, canCreateProjects, hasPermissionToCreateProjects, isTeamProjectFeatureEnabled, projectNavActiveId, setCurrentProject, getAllProjects, getMyProjects, getPersonalProject, getAvailableProjects, fetchProject, getProject, createProject, updateProject, deleteProject, getProjectsCount, setProjectNavActiveIdByWorkflowHomeProject, moveResourceToProject, }; });