Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make it possible to dynamically load node packages with dependencies #2849

Merged
merged 171 commits into from
Jul 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
171 commits
Select commit Hold shift + click to select a range
712a014
:sparkles: Make it possible to dynamically load node packages
janober Jan 2, 2022
2c35d8a
:zap: Fix comment
janober Jan 3, 2022
4a5e1d3
:sparkles: Make possible to dynamically install nodes from npm
janober Feb 20, 2022
402f55f
Created migration for sqlite regarding community nodes
krynble Mar 25, 2022
8b3c0ef
Saving to db whenever a package with nodes is installed
krynble Mar 25, 2022
371d7d9
Created endpoint to fetch installed packages
krynble Apr 7, 2022
bd920c3
WIP - uninstall package with nodes
krynble Apr 8, 2022
4d7861b
Fix lint issues
krynble Apr 11, 2022
83bb44b
Updating nodes via API
krynble Apr 14, 2022
492c503
Lint and improvement fixes
krynble Apr 19, 2022
73d7eb9
Created community node helpers and removed packages taht do not conta…
krynble Apr 20, 2022
17d7aa5
Check for package updates when fetching installed packages
krynble Apr 26, 2022
66ddd9b
Blocked access to non-owner and preventing incorrect install of packages
krynble Apr 26, 2022
2a1efbf
Added auto healing process
krynble Apr 27, 2022
2a3368c
Unit tests for helpers
krynble Apr 28, 2022
46545ee
Finishing tests for helpers
krynble Apr 28, 2022
7723495
Improved unit tests, refactored more helpers and created integration …
krynble May 2, 2022
ab85663
Implemented detection of missing packages on init and added warning t…
krynble May 6, 2022
ae012e0
Add check for banned packages and fix broken tests
krynble May 10, 2022
b0d9097
Create migrations for other db systems
krynble May 11, 2022
deb8b2f
Updated with latest changes from master
krynble May 23, 2022
a402c49
Fixed conflict errors
krynble May 30, 2022
073d33e
Improved unit tests, refactored more helpers and created integration …
krynble May 2, 2022
4ce6b39
Implemented detection of missing packages on init and added warning t…
krynble May 6, 2022
d38a4e4
🔥 Removing access check for the Settings sidebar item
MiloradFilipovic May 24, 2022
de68e0b
✨ Added inital community nodes settings screen
MiloradFilipovic May 24, 2022
8820491
⚡Added executionMode flag to settings
MiloradFilipovic May 24, 2022
115ccad
✨ Implemented N8N-callout component
MiloradFilipovic May 24, 2022
2a164b8
💄Updating Callout component template propery names
MiloradFilipovic May 24, 2022
de77dd8
💄 Updating Callout component styling.
MiloradFilipovic May 24, 2022
65eff32
💄Updating Callout component sizing and colors.
MiloradFilipovic May 24, 2022
32dc700
✔️ Updating Callout component test snapshots after styling changes
MiloradFilipovic May 24, 2022
304d2c4
✨ Updating the `ActionBox` component so it supports callouts and cond…
MiloradFilipovic May 25, 2022
235b55d
💄 Removing duplicate callout theme validation in the `ActionBox` comp…
MiloradFilipovic May 25, 2022
cf136f6
✨ Added warning message if instance is in the queue mode. Updated col…
MiloradFilipovic May 25, 2022
4b6874b
⚡ Added a custom permission support to router
MiloradFilipovic May 27, 2022
834e676
🔨 Implemented UM detection as a custom permission.
MiloradFilipovic May 27, 2022
f81eec2
👌Updating route permission logic.
MiloradFilipovic Jun 1, 2022
b355c20
✨ Implemented installed community packages list in the settings view
MiloradFilipovic Jun 1, 2022
913e286
👌 Updating settings routes rules and community nodes setting view.
MiloradFilipovic Jun 1, 2022
23f56c8
Allow installation of packages that failed to load
krynble May 25, 2022
9679e8e
👌 Updating `ActionBox`, `CommuntyPackageCard` components and settings…
MiloradFilipovic Jun 2, 2022
76df9c2
👌 Fixing community nodes loading state and sidebar icon spacing.
MiloradFilipovic Jun 2, 2022
95ed1d1
✨ Implemented loading skeletons for community package cards
MiloradFilipovic Jun 2, 2022
da1a108
👌 Handling errrors while loading installed package list. Updating spa…
MiloradFilipovic Jun 2, 2022
961ca39
👌 Updating community nodes error messages.
MiloradFilipovic Jun 2, 2022
52072c0
Merge pull request #3382 from n8n-io/N8N-3654-community-nodes-setting…
MiloradFilipovic Jun 3, 2022
1a0bcc0
Merge branch 'master' into node-sideload-npm-install
krynble Jun 8, 2022
a071e24
Added disable flag
krynble Jun 8, 2022
c8e0062
🐛 Fixing a community nodes update detection bug when there are missin…
MiloradFilipovic Jun 10, 2022
839989f
Merge pull request #3484 from n8n-io/n8n-3821-add-disable-feature-and…
MiloradFilipovic Jun 10, 2022
c59f93a
✨ Added front-end support for community nodes feature flag
MiloradFilipovic Jun 10, 2022
474b12d
✨ Implemented community package installation modal dialog
MiloradFilipovic Jun 2, 2022
66f7e39
💄 Community nodes installation modal updates: Moved links to constant…
MiloradFilipovic Jun 7, 2022
67beece
✨ Implemented community packages install flow
MiloradFilipovic Jun 8, 2022
8effb54
Standardize error codes (#3501)
krynble Jun 13, 2022
4a443f9
✨ Implemented community package installation modal dialog
MiloradFilipovic Jun 2, 2022
3d7c0bd
💄 Community nodes installation modal updates: Moved links to constant…
MiloradFilipovic Jun 7, 2022
c98facd
✨ Implemented community packages install flow
MiloradFilipovic Jun 8, 2022
5835618
Merge branch 'N8N-3656-install-community-package' of https://github.c…
MiloradFilipovic Jun 13, 2022
abf6678
✨ Updated error handling based on the response codes
MiloradFilipovic Jun 13, 2022
a5432a7
✨ Implemented community package installation modal dialog
MiloradFilipovic Jun 2, 2022
9fdce8c
✨ Implemented community package uninstall flow.
MiloradFilipovic Jun 8, 2022
91a90f0
✨ Finished update confirm modal UI
MiloradFilipovic Jun 10, 2022
ca6522f
💄 Replaced community nodes tooltip image with the one exported from f…
MiloradFilipovic Jun 10, 2022
43a625a
✨ Implemented community package update process
MiloradFilipovic Jun 13, 2022
cd6d651
✨ Updating community nodes list after successful package update
MiloradFilipovic Jun 13, 2022
298d5ac
Merge branch 'master' into node-sideload-npm-install
MiloradFilipovic Jun 13, 2022
da9e877
🔒 Updating public API setting route to use new access rules. Updating…
MiloradFilipovic Jun 13, 2022
d3dc535
✨ Implemented community package installation modal dialog
MiloradFilipovic Jun 2, 2022
4dde50c
💄 Community nodes installation modal updates: Moved links to constant…
MiloradFilipovic Jun 7, 2022
758284e
✨ Implemented community packages install flow
MiloradFilipovic Jun 8, 2022
f2af564
✨ Updated error handling based on the response codes
MiloradFilipovic Jun 13, 2022
635272a
Merge branch 'N8N-3656-install-community-package' of https://github.c…
MiloradFilipovic Jun 13, 2022
d6c9a07
Change output for installation request
krynble Jun 13, 2022
f36c58b
Improve payload for update requests
krynble Jun 14, 2022
b9acc7e
Merge pull request #3505 from n8n-io/n8n-3822-change-payload-for-inst…
MiloradFilipovic Jun 14, 2022
614fe41
👌 Updating community nodes install modal UI
MiloradFilipovic Jun 14, 2022
c633b30
Merge pull request #3480 from n8n-io/N8N-3656-install-community-package
MiloradFilipovic Jun 14, 2022
5c7ca66
👌 Updating community nodes confirm modal logic
MiloradFilipovic Jun 14, 2022
37ea486
👌 Refactoring community nodes confirm modal dialog
MiloradFilipovic Jun 14, 2022
5184c0e
👌 Separating community nodes components loading states
MiloradFilipovic Jun 14, 2022
6914e71
Merge branch 'node-sideload-npm-install' into N8N-3661-manage-communi…
MiloradFilipovic Jun 14, 2022
7529d31
💄 Updating community nodes install modal spacing.
MiloradFilipovic Jun 14, 2022
87f8154
Fix behavior for installing already installed packages
krynble Jun 14, 2022
e95f274
💡 Commenting community nodes install process
MiloradFilipovic Jun 14, 2022
ee19d0f
Merge pull request #3504 from n8n-io/N8N-3661-manage-community-packages
MiloradFilipovic Jun 14, 2022
cb5a67d
🔥 Removing leftover commits of deleted Vue mutations
MiloradFilipovic Jun 15, 2022
4e74e3b
✨ Updated node list to identify community nodes and handle node name …
MiloradFilipovic Jun 15, 2022
77cf9b6
✨ Implemented missing community node dialog.
MiloradFilipovic Jun 15, 2022
65e9ff5
💄 Updating n8n-tabs component to support tooltips
MiloradFilipovic Jun 16, 2022
82af831
✨ Updating node details with community node details.
MiloradFilipovic Jun 16, 2022
b425146
🔨 Using back-end response when updating community packages
MiloradFilipovic Jun 16, 2022
f9c9a9d
👌 Updating tabs component and refactoring community nodes store mutat…
MiloradFilipovic Jun 16, 2022
6fa7fff
👌 Adding community node flag to node type descriptions and using it t…
MiloradFilipovic Jun 16, 2022
35522d2
👌 Hiding unnecessary elements from missing node details panel.
MiloradFilipovic Jun 16, 2022
2693326
👌 Updating missing node type descriptions for custom and community nodes
MiloradFilipovic Jun 16, 2022
afd6290
👌 Updating community node package name detection logic
MiloradFilipovic Jun 16, 2022
4e31fab
👌 Removing communityNode flag from node description
MiloradFilipovic Jun 16, 2022
2aa6b8f
✨ Adding `force` flag to credentials fetching (#3527)
MiloradFilipovic Jun 16, 2022
e96b4e6
👌 Minor updates to community nodes details panel
MiloradFilipovic Jun 16, 2022
cf25aa4
Merge pull request #3523 from n8n-io/N8N-3662-community-nodes-node-vi…
MiloradFilipovic Jun 16, 2022
4492137
tests for post endpoint
agobrech Jun 14, 2022
c717b2b
duplicate comments
agobrech Jun 14, 2022
bb0ae1c
Add Patch and Delete enpoints tests
agobrech Jun 16, 2022
3a3c850
🔒 Using `pageCategory`prop to assemble the list of settings routes in…
MiloradFilipovic Jun 20, 2022
62ca12e
📈 Added front-end telemetry events for community nodes
MiloradFilipovic Jun 20, 2022
091938b
Merge branch 'node-sideload-npm-install' of https://github.com/n8n-io…
MiloradFilipovic Jun 20, 2022
e26c7d1
Merge master
krynble Jun 20, 2022
b9b2520
Merge branch 'node-sideload-npm-install' of github.com:n8n-io/n8n int…
krynble Jun 20, 2022
d53d3fc
📈 Updating community nodes telemetry events
MiloradFilipovic Jun 20, 2022
c6514c8
Merge branch 'node-sideload-npm-install' of https://github.com/n8n-io…
MiloradFilipovic Jun 20, 2022
6ec6139
💄 Updating community nodes settings UI elements based on product/desi…
MiloradFilipovic Jun 21, 2022
47c393b
💄 Updating node view & node details view for community nodes based on…
MiloradFilipovic Jun 21, 2022
7cd1fcd
💄 Fixing community node text capitalisation
MiloradFilipovic Jun 21, 2022
1859634
✨ Adding community node install error message under the package name …
MiloradFilipovic Jun 21, 2022
2890f86
Fixed and improved tests
krynble Jun 22, 2022
0968930
Merge branch 'node-sideload-npm-install' of github.com:n8n-io/n8n int…
krynble Jun 22, 2022
c94a55b
Fix lint issue
krynble Jun 22, 2022
003ae93
Merge branch 'master' into node-sideload-npm-install
MiloradFilipovic Jun 23, 2022
73462f0
feat: Migrated to npm release of riot-tmpl fork.
alexgrozav Jun 23, 2022
c327528
📈 Updating community nodes telemetry events based on the product review
MiloradFilipovic Jun 23, 2022
91c08f6
💄 Updating community nodes UI based on the design feedback
MiloradFilipovic Jun 23, 2022
ed97cbf
🔀 Merging recent node draggable panels changes
MiloradFilipovic Jun 23, 2022
43c278a
Implement self healing process
krynble Jun 23, 2022
daef34e
Improve error messages for package name requirement and disk space
krynble Jun 23, 2022
01eee7a
Merge branch 'node-sideload-npm-install' of github.com:n8n-io/n8n int…
krynble Jun 23, 2022
893745a
💄 Removing front-end error message override since appropriate respons…
MiloradFilipovic Jun 23, 2022
ff8444c
Merge branch 'n8n-3956-change-riot-tmpl-fork-installation-to' into no…
krynble Jun 23, 2022
0152487
Fix lint issues
krynble Jun 23, 2022
87ce4f0
Fix installed node name
krynble Jun 23, 2022
c7fd808
💄 Removed additional node name parsing
MiloradFilipovic Jun 23, 2022
e196a81
📈 Updating community nodes telemetry events
MiloradFilipovic Jun 23, 2022
d1c1a8c
Fix postgres migration for cascading nodes when package is removed
krynble Jun 23, 2022
3e08445
Merge branch 'node-sideload-npm-install' of github.com:n8n-io/n8n int…
krynble Jun 23, 2022
dddaff3
Remove postman mock for banned packages
krynble Jun 23, 2022
cf99ee4
📈 Adding missing telemetry event for community node documentation click
MiloradFilipovic Jun 24, 2022
f363479
Merge branch 'node-sideload-npm-install' of https://github.com/n8n-io…
MiloradFilipovic Jun 24, 2022
feb924a
Merge branch 'master' into node-sideload-npm-install
MiloradFilipovic Jun 24, 2022
e770864
🐛 Fixing community nodes UI bugs reported during the bug bash
MiloradFilipovic Jun 24, 2022
c4b0371
Fix issue with uninstalling packages not reflecting UI
krynble Jun 27, 2022
dfbc601
Merge branch 'node-sideload-npm-install' of github.com:n8n-io/n8n int…
krynble Jun 27, 2022
54df161
🐛 Fixing a missing node type bug when trying to run a workflow.
MiloradFilipovic Jun 27, 2022
7f6ffd8
Improve error detection for installing packages
krynble Jun 27, 2022
6d5d421
Merge branch 'node-sideload-npm-install' of github.com:n8n-io/n8n int…
krynble Jun 27, 2022
7755d72
Merge branch 'master' into node-sideload-npm-install
krynble Jun 27, 2022
6708917
Merge branch 'master' into node-sideload-npm-install
krynble Jun 30, 2022
834352b
💄 Updating community nodes components styling and wording based on th…
MiloradFilipovic Jun 30, 2022
2f15945
Implement telemetry be events
krynble Jun 30, 2022
b961b1a
Merge branch 'node-sideload-npm-install' of github.com:n8n-io/n8n int…
krynble Jun 30, 2022
6ab44af
Add author name and email to packages
krynble Jun 30, 2022
4a40040
Fix telemetry be events for community packages
krynble Jun 30, 2022
bbd9bf7
📈 Updating front-end telemetry events with community nodes author data
MiloradFilipovic Jun 30, 2022
92816ca
Merge master
krynble Jul 4, 2022
cbcc1a1
💄 Updating credentials documentation link logic to handle community n…
MiloradFilipovic Jul 5, 2022
282a415
Merge branch 'master' into node-sideload-npm-install
krynble Jul 5, 2022
cc073bc
🐛 Fixing draggable panels logic
MiloradFilipovic Jul 5, 2022
d50d566
Fix duplicate wrong import
krynble Jul 5, 2022
35ff379
Merge branch 'node-sideload-npm-install' of github.com:n8n-io/n8n int…
krynble Jul 5, 2022
ad0e484
💄 Hiding community nodes credentials documentation links when they do…
MiloradFilipovic Jul 5, 2022
ec30791
Fix issue with detection of missing packages
krynble Jul 5, 2022
49f69d8
Merge branch 'node-sideload-npm-install' of github.com:n8n-io/n8n int…
krynble Jul 5, 2022
9f089a0
💄 Adding the `Docs` tab to community nodes
MiloradFilipovic Jul 6, 2022
21782cf
💄 Adding a failed loading indicator to community nodes list
MiloradFilipovic Jul 6, 2022
74a005c
Prevent n8n from crashing on startup
krynble Jul 6, 2022
05448fd
Merge branch 'node-sideload-npm-install' of github.com:n8n-io/n8n int…
krynble Jul 6, 2022
1b44397
Refactor and improve code quality
krynble Jul 15, 2022
9c7437e
Merge master
krynble Jul 15, 2022
3b8ff29
Merge master
krynble Jul 20, 2022
0093b64
Merge master and fix conflicts
krynble Jul 20, 2022
a4f62f4
:zap: Remove not needed depenedency
janober Jul 20, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions packages/cli/commands/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
} from '../src';

import { getLogger } from '../src/Logger';
import { getAllInstalledPackages } from '../src/CommunityNodes/packageModel';

// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires
const open = require('open');
Expand Down Expand Up @@ -60,6 +61,10 @@ export class Start extends Command {
description:
'runs the webhooks via a hooks.n8n.cloud tunnel server. Use only for testing and development!',
}),
reinstallMissingPackages: flags.boolean({
description:
'Attempts to self heal n8n if packages with nodes are missing. Might drastically increase startup times.',
}),
};

/**
Expand Down Expand Up @@ -206,6 +211,23 @@ export class Start extends Command {
// Wait till the database is ready
await startDbInitPromise;

const installedPackages = await getAllInstalledPackages();
const missingPackages = new Set<{
packageName: string;
version: string;
}>();
installedPackages.forEach((installedpackage) => {
installedpackage.installedNodes.forEach((installedNode) => {
if (!loadNodesAndCredentials.nodeTypes[installedNode.type]) {
// Leave the list ready for installing in case we need.
missingPackages.add({
packageName: installedpackage.packageName,
version: installedpackage.installedVersion,
});
}
});
});

await UserSettings.getEncryptionKey();

// Load settings from database and set them to config.
Expand All @@ -214,6 +236,42 @@ export class Start extends Command {
config.set(setting.key, JSON.parse(setting.value));
});

config.set('nodes.packagesMissing', '');
if (missingPackages.size) {
LoggerProxy.error(
'n8n detected that some packages are missing. For more information, visit https://docs.n8n.io/integrations/community-nodes/troubleshooting/',
);

if (flags.reinstallMissingPackages || process.env.N8N_REINSTALL_MISSING_PACKAGES) {
LoggerProxy.info('Attempting to reinstall missing packages', { missingPackages });
try {
// Optimistic approach - stop if any installation fails
// eslint-disable-next-line no-restricted-syntax
for (const missingPackage of missingPackages) {
// eslint-disable-next-line no-await-in-loop
void (await loadNodesAndCredentials.loadNpmModule(
missingPackage.packageName,
missingPackage.version,
));
missingPackages.delete(missingPackage);
}
LoggerProxy.info(
'Packages reinstalled successfully. Resuming regular intiailization.',
);
} catch (error) {
LoggerProxy.error('n8n was unable to install the missing packages.');
}
}
}
if (missingPackages.size) {
config.set(
'nodes.packagesMissing',
Array.from(missingPackages)
.map((missingPackage) => `${missingPackage.packageName}@${missingPackage.version}`)
.join(' '),
);
}

if (config.getEnv('executions.mode') === 'queue') {
const redisHost = config.getEnv('queue.bull.redis.host');
const redisPassword = config.getEnv('queue.bull.redis.password');
Expand Down
8 changes: 8 additions & 0 deletions packages/cli/config/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,14 @@ export const schema = {
default: 'n8n-nodes-base.errorTrigger',
env: 'NODES_ERROR_TRIGGER_TYPE',
},
communityPackages: {
enabled: {
doc: 'Allows you to disable the usage of community packages for nodes',
format: Boolean,
default: true,
env: 'N8N_COMMUNITY_PACKAGES_ENABLED',
},
},
},

logs: {
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@
"@types/supertest": "^2.0.11",
"@types/uuid": "^8.3.0",
"@types/validator": "^13.7.0",
"axios": "^0.21.1",
"concurrently": "^5.1.0",
"jest": "^27.4.7",
"nodemon": "^2.0.2",
Expand All @@ -109,6 +108,7 @@
"@types/shelljs": "^0.8.11",
"@types/swagger-ui-express": "^4.1.3",
"@types/yamljs": "^0.2.31",
"axios": "^0.21.1",
"basic-auth": "^2.0.1",
"bcryptjs": "^2.4.3",
"body-parser": "^1.18.3",
Expand Down
218 changes: 218 additions & 0 deletions packages/cli/src/CommunityNodes/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
/* eslint-disable import/no-cycle */
/* eslint-disable @typescript-eslint/naming-convention */
import { promisify } from 'util';
import { exec } from 'child_process';
import { access as fsAccess, mkdir as fsMkdir } from 'fs/promises';

import { UserSettings } from 'n8n-core';
import { LoggerProxy, PublicInstalledPackage } from 'n8n-workflow';
import axios from 'axios';
import {
NODE_PACKAGE_PREFIX,
NPM_COMMAND_TOKENS,
NPM_PACKAGE_STATUS_GOOD,
RESPONSE_ERROR_MESSAGES,
} from '../constants';
import { NpmPackageStatusCheck, NpmUpdatesAvailable, ParsedNpmPackageName } from '../Interfaces';
import { InstalledPackages } from '../databases/entities/InstalledPackages';
import config from '../../config';

const execAsync = promisify(exec);

export const parsePackageName = (originalString: string | undefined): ParsedNpmPackageName => {
if (!originalString) {
throw new Error('Package name was not provided');
}

if (new RegExp(/[^0-9a-z@\-./]/).test(originalString)) {
// Prevent any strings that are not valid npm package names or
// could indicate malicous commands
throw new Error('Package name must be a single word');
}

const scope = originalString.includes('/') ? originalString.split('/')[0] : undefined;

const packageNameWithoutScope = scope ? originalString.replace(`${scope}/`, '') : originalString;

if (!packageNameWithoutScope.startsWith(NODE_PACKAGE_PREFIX)) {
throw new Error('Package name must start with n8n-nodes-');
}

const version = packageNameWithoutScope.includes('@')
? packageNameWithoutScope.split('@')[1]
: undefined;

const packageName = version ? originalString.replace(`@${version}`, '') : originalString;

return {
packageName,
scope,
version,
originalString,
};
};

export const executeCommand = async (
command: string,
options?: {
doNotHandleError?: boolean;
},
): Promise<string> => {
const downloadFolder = UserSettings.getUserN8nFolderDowloadedNodesPath();
// Make sure the node-download folder exists
try {
await fsAccess(downloadFolder);
// eslint-disable-next-line no-empty
} catch (error) {
await fsMkdir(downloadFolder);
}
const execOptions = {
cwd: downloadFolder,
env: {
NODE_PATH: process.env.NODE_PATH,
PATH: process.env.PATH,
},
};

try {
const commandResult = await execAsync(command, execOptions);
return commandResult.stdout;
} catch (error) {
if (options?.doNotHandleError) {
throw error;
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const errorMessage = error.message as string;

if (
errorMessage.includes(NPM_COMMAND_TOKENS.NPM_PACKAGE_NOT_FOUND_ERROR) ||
errorMessage.includes(NPM_COMMAND_TOKENS.NPM_NO_VERSION_AVAILABLE)
) {
throw new Error(RESPONSE_ERROR_MESSAGES.PACKAGE_NOT_FOUND);
}
if (errorMessage.includes(NPM_COMMAND_TOKENS.NPM_PACKAGE_VERSION_NOT_FOUND_ERROR)) {
throw new Error(RESPONSE_ERROR_MESSAGES.PACKAGE_VERSION_NOT_FOUND);
}
if (
errorMessage.includes(NPM_COMMAND_TOKENS.NPM_DISK_NO_SPACE) ||
errorMessage.includes(NPM_COMMAND_TOKENS.NPM_DISK_INSUFFICIENT_SPACE)
) {
throw new Error(RESPONSE_ERROR_MESSAGES.DISK_IS_FULL);
}

// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
LoggerProxy.warn('npm command failed; see message', { errorMessage });

throw new Error('Package could not be installed - check logs for details');
}
};

export function matchPackagesWithUpdates(
installedPackages: InstalledPackages[],
availableUpdates?: NpmUpdatesAvailable,
): PublicInstalledPackage[] {
if (!availableUpdates) {
return installedPackages;
}
const hydratedPackageList = [] as PublicInstalledPackage[];

for (let i = 0; i < installedPackages.length; i++) {
const installedPackage = installedPackages[i];
const publicPackage = { ...installedPackage } as PublicInstalledPackage;

if (availableUpdates[installedPackage.packageName]) {
publicPackage.updateAvailable = availableUpdates[installedPackage.packageName].latest;
}
hydratedPackageList.push(publicPackage);
}

return hydratedPackageList;
}

export function matchMissingPackages(
installedPackages: PublicInstalledPackage[],
missingPackages: string,
): PublicInstalledPackage[] {
const missingPackageNames = missingPackages.split(' ');

const missingPackagesList = missingPackageNames.map((missingPackageName: string) => {
// Strip away versions but maintain scope and package name
try {
const parsedPackageData = parsePackageName(missingPackageName);
return parsedPackageData.packageName;

// eslint-disable-next-line no-empty
} catch (_) {}
return undefined;
});

const hydratedPackageList = [] as PublicInstalledPackage[];
installedPackages.forEach((installedPackage) => {
const hydratedInstalledPackage = { ...installedPackage };
if (missingPackagesList.includes(hydratedInstalledPackage.packageName)) {
hydratedInstalledPackage.failedLoading = true;
}
hydratedPackageList.push(hydratedInstalledPackage);
});

return hydratedPackageList;
}

export async function checkPackageStatus(packageName: string): Promise<NpmPackageStatusCheck> {
// You can change this URL for testing - the default testing url below
// is a postman mock service
const n8nBackendServiceUrl = 'https://api.n8n.io/api/package';

try {
const output = await axios.post(
n8nBackendServiceUrl,
{ name: packageName },
{
method: 'POST',
},
);

// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
if (output.data.status !== NPM_PACKAGE_STATUS_GOOD) {
return output.data as NpmPackageStatusCheck;
}
} catch (error) {
// Do nothing if service is unreachable
}
return { status: NPM_PACKAGE_STATUS_GOOD };
}

export function hasPackageLoadedSuccessfully(packageName: string): boolean {
try {
const failedPackages = (config.get('nodes.packagesMissing') as string).split(' ');

const packageFailedToLoad = failedPackages.find(
(packageNameAndVersion) =>
packageNameAndVersion.startsWith(packageName) &&
packageNameAndVersion.replace(packageName, '').startsWith('@'),
);
if (packageFailedToLoad) {
return false;
}
return true;
} catch (_error) {
// If key doesn't exist it means all packages loaded fine
return true;
}
}

export function removePackageFromMissingList(packageName: string): void {
try {
const failedPackages = (config.get('nodes.packagesMissing') as string).split(' ');

const packageFailedToLoad = failedPackages.filter(
(packageNameAndVersion) =>
!packageNameAndVersion.startsWith(packageName) ||
!packageNameAndVersion.replace(packageName, '').startsWith('@'),
);

config.set('nodes.packagesMissing', packageFailedToLoad.join(' '));
} catch (_error) {
// Do nothing
}
}
Loading