mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
fix(core): Stop copying icons to cache (#5419)
Fixes - https://github.com/n8n-io/n8n/issues/4973 - https://github.com/n8n-io/n8n/issues/5274 - https://community.n8n.io/t/starting-n8n-fails-with-ebusy-error/21243 - https://community.n8n.io/t/problem-executing-workflow-ebusy-resource-busy-or-locked-copyfile/21280 Replaces - https://github.com/n8n-io/n8n/pull/5052 - https://github.com/n8n-io/n8n/pull/5401
This commit is contained in:
committed by
GitHub
parent
1f924e3c3d
commit
f23fb92696
@@ -18,13 +18,7 @@ import type {
|
||||
import { LoggerProxy, ErrorReporterProxy as ErrorReporter } from 'n8n-workflow';
|
||||
|
||||
import { createWriteStream } from 'fs';
|
||||
import {
|
||||
access as fsAccess,
|
||||
copyFile,
|
||||
mkdir,
|
||||
readdir as fsReaddir,
|
||||
stat as fsStat,
|
||||
} from 'fs/promises';
|
||||
import { access as fsAccess, mkdir, readdir as fsReaddir, stat as fsStat } from 'fs/promises';
|
||||
import path from 'path';
|
||||
import config from '@/config';
|
||||
import type { InstalledPackages } from '@db/entities/InstalledPackages';
|
||||
@@ -50,6 +44,8 @@ export class LoadNodesAndCredentialsClass implements INodesAndCredentials {
|
||||
|
||||
types: Types = { nodes: [], credentials: [] };
|
||||
|
||||
loaders: Record<string, DirectoryLoader> = {};
|
||||
|
||||
excludeNodes = config.getEnv('nodes.exclude');
|
||||
|
||||
includeNodes = config.getEnv('nodes.include');
|
||||
@@ -73,6 +69,7 @@ export class LoadNodesAndCredentialsClass implements INodesAndCredentials {
|
||||
await this.loadNodesFromBasePackages();
|
||||
await this.loadNodesFromDownloadedPackages();
|
||||
await this.loadNodesFromCustomDirectories();
|
||||
await this.postProcessLoaders();
|
||||
this.injectCustomApiCallOptions();
|
||||
}
|
||||
|
||||
@@ -117,7 +114,7 @@ export class LoadNodesAndCredentialsClass implements INodesAndCredentials {
|
||||
await writeStaticJSON('credentials', this.types.credentials);
|
||||
}
|
||||
|
||||
async loadNodesFromBasePackages() {
|
||||
private async loadNodesFromBasePackages() {
|
||||
const nodeModulesPath = await this.getNodeModulesPath();
|
||||
const nodePackagePaths = await this.getN8nNodePackages(nodeModulesPath);
|
||||
|
||||
@@ -126,7 +123,7 @@ export class LoadNodesAndCredentialsClass implements INodesAndCredentials {
|
||||
}
|
||||
}
|
||||
|
||||
async loadNodesFromDownloadedPackages(): Promise<void> {
|
||||
private async loadNodesFromDownloadedPackages(): Promise<void> {
|
||||
const nodePackages = [];
|
||||
try {
|
||||
// Read downloaded nodes and credentials
|
||||
@@ -160,24 +157,23 @@ export class LoadNodesAndCredentialsClass implements INodesAndCredentials {
|
||||
return customDirectories;
|
||||
}
|
||||
|
||||
async loadNodesFromCustomDirectories(): Promise<void> {
|
||||
private async loadNodesFromCustomDirectories(): Promise<void> {
|
||||
for (const directory of this.getCustomDirectories()) {
|
||||
await this.runDirectoryLoader(CustomDirectoryLoader, directory);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the names of the packages which could
|
||||
* contain n8n nodes
|
||||
*
|
||||
* Returns all the names of the packages which could contain n8n nodes
|
||||
*/
|
||||
async getN8nNodePackages(baseModulesPath: string): Promise<string[]> {
|
||||
private async getN8nNodePackages(baseModulesPath: string): Promise<string[]> {
|
||||
const getN8nNodePackagesRecursive = async (relativePath: string): Promise<string[]> => {
|
||||
const results: string[] = [];
|
||||
const nodeModulesPath = `${baseModulesPath}/${relativePath}`;
|
||||
for (const file of await fsReaddir(nodeModulesPath)) {
|
||||
const isN8nNodesPackage = file.indexOf('n8n-nodes-') === 0;
|
||||
const isNpmScopedPackage = file.indexOf('@') === 0;
|
||||
const nodeModules = await fsReaddir(nodeModulesPath);
|
||||
for (const nodeModule of nodeModules) {
|
||||
const isN8nNodesPackage = nodeModule.indexOf('n8n-nodes-') === 0;
|
||||
const isNpmScopedPackage = nodeModule.indexOf('@') === 0;
|
||||
if (!isN8nNodesPackage && !isNpmScopedPackage) {
|
||||
continue;
|
||||
}
|
||||
@@ -185,10 +181,10 @@ export class LoadNodesAndCredentialsClass implements INodesAndCredentials {
|
||||
continue;
|
||||
}
|
||||
if (isN8nNodesPackage) {
|
||||
results.push(`${baseModulesPath}/${relativePath}${file}`);
|
||||
results.push(`${baseModulesPath}/${relativePath}${nodeModule}`);
|
||||
}
|
||||
if (isNpmScopedPackage) {
|
||||
results.push(...(await getN8nNodePackagesRecursive(`${relativePath}${file}/`)));
|
||||
results.push(...(await getN8nNodePackagesRecursive(`${relativePath}${nodeModule}/`)));
|
||||
}
|
||||
}
|
||||
return results;
|
||||
@@ -392,64 +388,52 @@ export class LoadNodesAndCredentialsClass implements INodesAndCredentials {
|
||||
) {
|
||||
const loader = new constructor(dir, this.excludeNodes, this.includeNodes);
|
||||
await loader.loadAll();
|
||||
|
||||
// list of node & credential types that will be sent to the frontend
|
||||
const { types } = loader;
|
||||
this.types.nodes = this.types.nodes.concat(types.nodes);
|
||||
this.types.credentials = this.types.credentials.concat(types.credentials);
|
||||
|
||||
// Copy over all icons and set `iconUrl` for the frontend
|
||||
const iconPromises = Object.entries(types).flatMap(([typeName, typesArr]) =>
|
||||
typesArr.map((type) => {
|
||||
if (!type.icon?.startsWith('file:')) return;
|
||||
const icon = type.icon.substring(5);
|
||||
const iconUrl = `icons/${typeName}/${type.name}${path.extname(icon)}`;
|
||||
delete type.icon;
|
||||
type.iconUrl = iconUrl;
|
||||
const source = path.join(dir, icon);
|
||||
const destination = path.join(GENERATED_STATIC_DIR, iconUrl);
|
||||
return mkdir(path.dirname(destination), { recursive: true }).then(async () =>
|
||||
copyFile(source, destination),
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
await Promise.all(iconPromises);
|
||||
|
||||
// Nodes and credentials that have been loaded immediately
|
||||
for (const nodeTypeName in loader.nodeTypes) {
|
||||
this.loaded.nodes[nodeTypeName] = loader.nodeTypes[nodeTypeName];
|
||||
}
|
||||
|
||||
for (const credentialTypeName in loader.credentialTypes) {
|
||||
this.loaded.credentials[credentialTypeName] = loader.credentialTypes[credentialTypeName];
|
||||
}
|
||||
|
||||
// Nodes and credentials that will be lazy loaded
|
||||
if (loader instanceof PackageDirectoryLoader) {
|
||||
const { packageName, known } = loader;
|
||||
|
||||
for (const type in known.nodes) {
|
||||
const { className, sourcePath } = known.nodes[type];
|
||||
this.known.nodes[type] = {
|
||||
className,
|
||||
sourcePath: path.join(dir, sourcePath),
|
||||
};
|
||||
}
|
||||
|
||||
for (const type in known.credentials) {
|
||||
const { className, sourcePath, nodesToTestWith } = known.credentials[type];
|
||||
this.known.credentials[type] = {
|
||||
className,
|
||||
sourcePath: path.join(dir, sourcePath),
|
||||
nodesToTestWith: nodesToTestWith?.map((nodeName) => `${packageName}.${nodeName}`),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
this.loaders[dir] = loader;
|
||||
return loader;
|
||||
}
|
||||
|
||||
async postProcessLoaders() {
|
||||
this.types.nodes = [];
|
||||
this.types.credentials = [];
|
||||
for (const [dir, loader] of Object.entries(this.loaders)) {
|
||||
// list of node & credential types that will be sent to the frontend
|
||||
const { types } = loader;
|
||||
this.types.nodes = this.types.nodes.concat(types.nodes);
|
||||
this.types.credentials = this.types.credentials.concat(types.credentials);
|
||||
|
||||
// Nodes and credentials that have been loaded immediately
|
||||
for (const nodeTypeName in loader.nodeTypes) {
|
||||
this.loaded.nodes[nodeTypeName] = loader.nodeTypes[nodeTypeName];
|
||||
}
|
||||
|
||||
for (const credentialTypeName in loader.credentialTypes) {
|
||||
this.loaded.credentials[credentialTypeName] = loader.credentialTypes[credentialTypeName];
|
||||
}
|
||||
|
||||
// Nodes and credentials that will be lazy loaded
|
||||
if (loader instanceof PackageDirectoryLoader) {
|
||||
const { packageName, known } = loader;
|
||||
|
||||
for (const type in known.nodes) {
|
||||
const { className, sourcePath } = known.nodes[type];
|
||||
this.known.nodes[type] = {
|
||||
className,
|
||||
sourcePath: path.join(dir, sourcePath),
|
||||
};
|
||||
}
|
||||
|
||||
for (const type in known.credentials) {
|
||||
const { className, sourcePath, nodesToTestWith } = known.credentials[type];
|
||||
this.known.credentials[type] = {
|
||||
className,
|
||||
sourcePath: path.join(dir, sourcePath),
|
||||
nodesToTestWith: nodesToTestWith?.map((nodeName) => `${packageName}.${nodeName}`),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async getNodeModulesPath(): Promise<string> {
|
||||
// Get the path to the node-modules folder to be later able
|
||||
// to load the credentials and nodes
|
||||
|
||||
Reference in New Issue
Block a user