n8n
Version:
n8n Workflow Automation Tool
194 lines • 9.33 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CommunityNodeTypesService = void 0;
const backend_common_1 = require("@n8n/backend-common");
const di_1 = require("@n8n/di");
const n8n_workflow_1 = require("n8n-workflow");
const cloneDeep_1 = __importDefault(require("lodash/cloneDeep"));
const community_node_types_utils_1 = require("./community-node-types-utils");
const community_packages_config_1 = require("./community-packages.config");
const community_packages_service_1 = require("./community-packages.service");
const strapi_utils_1 = require("./strapi-utils");
const UPDATE_INTERVAL = 8 * 60 * 60 * 1000;
const RETRY_INTERVAL = 5 * 60 * 1000;
const STRAPI_ARRAY_LIMIT = 100;
let CommunityNodeTypesService = class CommunityNodeTypesService {
constructor(logger, config, communityPackagesService) {
this.logger = logger;
this.config = config;
this.communityPackagesService = communityPackagesService;
this.communityNodeTypes = new Map();
this.lastUpdateTimestamp = 0;
}
async detectUpdates(environment) {
let communityNodesMetadata = [];
try {
communityNodesMetadata = await (0, community_node_types_utils_1.getCommunityNodesMetadata)(environment, this.config.aiNodeSdkVersion);
}
catch (error) {
this.logger.error('Failed to fetch community nodes metadata', {
error: (0, n8n_workflow_1.ensureError)(error),
});
return { scheduleRetry: true };
}
const typesToUpdate = [];
const metadataNames = new Set(communityNodesMetadata.map((entry) => entry.name));
for (const entry of communityNodesMetadata) {
const nodeType = this.communityNodeTypes.get(entry.name);
if (!nodeType ||
nodeType.npmVersion !== entry.npmVersion ||
nodeType.updatedAt !== entry.updatedAt) {
this.logger.debug(`Detected update for community node type: name - ${entry.name}; npmVersion - ${entry.npmVersion}; updatedAt - ${entry.updatedAt};`);
typesToUpdate.push(entry.id);
}
}
for (const [name, nodeType] of this.communityNodeTypes.entries()) {
if (!metadataNames.has(name)) {
this.logger.debug(`Detected removal of community node type: name - ${name}; id - ${nodeType.id};`);
this.communityNodeTypes.delete(name);
}
}
return { typesToUpdate };
}
async fetchNodeTypes() {
try {
const environment = this.detectEnvironment();
let data = [];
if (this.config.enabled && this.config.verifiedEnabled) {
if (this.communityNodeTypes.size === 0) {
data = await (0, community_node_types_utils_1.getCommunityNodeTypes)(environment, {}, this.config.aiNodeSdkVersion);
this.updateCommunityNodeTypes(data);
return;
}
const { typesToUpdate, scheduleRetry } = await this.detectUpdates(environment);
if (scheduleRetry) {
this.setTimestampForRetry();
return;
}
if (!typesToUpdate?.length) {
this.lastUpdateTimestamp = Date.now();
return;
}
for (let i = 0; i < typesToUpdate.length; i += STRAPI_ARRAY_LIMIT) {
const batch = typesToUpdate.slice(i, i + STRAPI_ARRAY_LIMIT);
const qs = (0, strapi_utils_1.buildStrapiUpdateQuery)(batch);
const batchData = await (0, community_node_types_utils_1.getCommunityNodeTypes)(environment, qs, this.config.aiNodeSdkVersion);
data.push(...batchData);
}
}
this.updateCommunityNodeTypes(data);
}
catch (error) {
this.logger.error('Failed to fetch community node types', { error: (0, n8n_workflow_1.ensureError)(error) });
}
}
detectEnvironment() {
const environment = process.env.ENVIRONMENT;
if (environment === 'staging')
return 'staging';
if (backend_common_1.inProduction)
return 'production';
if (environment === 'production')
return 'production';
return 'staging';
}
updateCommunityNodeTypes(nodeTypes) {
if (!nodeTypes?.length) {
this.setTimestampForRetry();
return;
}
this.setCommunityNodeTypes(nodeTypes);
this.createAiTools();
this.lastUpdateTimestamp = Date.now();
}
createAiTools() {
const usableAsTools = Array.from(this.communityNodeTypes.values()).filter((nodeType) => nodeType.nodeDescription.usableAsTool &&
!(0, n8n_workflow_1.isToolType)(nodeType.name) &&
!nodeType.nodeDescription.group?.includes('trigger'));
const forbiddenCategories = ['Recommended Tools'];
for (const nodeType of usableAsTools) {
const clonedNodeType = (0, cloneDeep_1.default)(nodeType);
const toolSubcategories = clonedNodeType.nodeDescription.codex?.subcategories?.Tools ?? [
'Other Tools',
];
const filteredToolSubcategories = toolSubcategories.filter((subcategory) => !forbiddenCategories.includes(subcategory));
clonedNodeType.name += 'Tool';
clonedNodeType.nodeDescription.name += 'Tool';
clonedNodeType.nodeDescription.inputs = [];
clonedNodeType.nodeDescription.outputs = [n8n_workflow_1.NodeConnectionTypes.AiTool];
clonedNodeType.nodeDescription.displayName += ' Tool';
clonedNodeType.nodeDescription.codex = {
categories: ['AI'],
subcategories: {
AI: ['Tools'],
Tools: filteredToolSubcategories,
},
resources: clonedNodeType.nodeDescription.codex?.resources ?? {},
};
this.communityNodeTypes.set(clonedNodeType.name, clonedNodeType);
}
}
setCommunityNodeTypes(nodeTypes) {
for (const nodeType of nodeTypes) {
this.communityNodeTypes.set(nodeType.name, nodeType);
}
}
updateRequired() {
if (!this.lastUpdateTimestamp)
return true;
return Date.now() - this.lastUpdateTimestamp > UPDATE_INTERVAL;
}
setTimestampForRetry() {
const jitter = Math.floor(Math.random() * 4 * 60 * 1000) - 2 * 60 * 1000;
this.lastUpdateTimestamp = Date.now() - (UPDATE_INTERVAL - RETRY_INTERVAL + jitter);
}
async createIsInstalled() {
const installedPackages = (await this.communityPackagesService.getAllInstalledPackages()) ?? [];
const installedPackageNames = new Set(installedPackages.map((p) => p.packageName));
return (nodeTypeName) => installedPackageNames.has(nodeTypeName.split('.')[0]);
}
async getCommunityNodeTypes() {
if (this.updateRequired()) {
await this.fetchNodeTypes();
}
const isInstalled = await this.createIsInstalled();
return Array.from(this.communityNodeTypes.values()).map((nodeType) => ({
...nodeType,
isInstalled: isInstalled(nodeType.name),
}));
}
async getCommunityNodeType(type) {
const nodeType = this.communityNodeTypes.get(type);
const isInstalled = await this.createIsInstalled();
if (!nodeType)
return null;
return { ...nodeType, isInstalled: isInstalled(nodeType.name) };
}
async findVetted(packageName) {
if (this.updateRequired()) {
await this.fetchNodeTypes();
}
const vettedTypes = Array.from(this.communityNodeTypes.values());
return vettedTypes.find((nodeType) => nodeType.packageName === packageName);
}
};
exports.CommunityNodeTypesService = CommunityNodeTypesService;
exports.CommunityNodeTypesService = CommunityNodeTypesService = __decorate([
(0, di_1.Service)(),
__metadata("design:paramtypes", [backend_common_1.Logger,
community_packages_config_1.CommunityPackagesConfig,
community_packages_service_1.CommunityPackagesService])
], CommunityNodeTypesService);
//# sourceMappingURL=community-node-types.service.js.map