n8n
Version:
n8n Workflow Automation Tool
163 lines • 8.61 kB
JavaScript
;
const api_types_1 = require("@n8n/api-types");
const di_1 = require("@n8n/di");
const n8n_workflow_1 = require("n8n-workflow");
const bad_request_error_1 = require("../../../../errors/response-errors/bad-request.error");
const forbidden_error_1 = require("../../../../errors/response-errors/forbidden.error");
const event_service_1 = require("../../../../events/event.service");
const package_export_errors_1 = require("../../../../modules/n8n-packages/entities/package-export.errors");
const n8n_packages_service_1 = require("../../../../modules/n8n-packages/n8n-packages.service");
const package_failure_classifier_1 = require("../../../../modules/n8n-packages/package-failure-classifier");
const import_package_upload_1 = require("../../../../modules/n8n-packages/utils/import-package-upload");
const global_middleware_1 = require("../../shared/middlewares/global.middleware");
const PACKAGE_EXPORT_SCOPES = 'project:export,workflow:export';
const EXPORT_COUNTS_HEADER = 'X-N8n-Export-Counts';
function assertPackageExportApiKeyScopes(req, workflowIds, folderIds, projectIds) {
const apiKeyScopes = req.tokenGrant?.apiKeyScopes;
if (!apiKeyScopes) {
throw new forbidden_error_1.ForbiddenError('Forbidden');
}
const requiredScopes = [];
if (workflowIds.length > 0 || folderIds.length > 0) {
requiredScopes.push('workflow:export');
}
if (projectIds.length > 0) {
requiredScopes.push('project:export');
}
for (const scope of requiredScopes) {
if (!apiKeyScopes.includes(scope)) {
throw new forbidden_error_1.ForbiddenError('Forbidden');
}
}
return apiKeyScopes;
}
function assertPackageImportApiKeyScopes(req) {
const apiKeyScopes = req.tokenGrant?.apiKeyScopes;
if (!apiKeyScopes?.includes('workflow:import')) {
throw new forbidden_error_1.ForbiddenError('Forbidden');
}
}
async function streamPackageExport(res, { stream, counts }) {
res.setHeader('Content-Type', 'application/gzip');
res.setHeader('Content-Disposition', 'attachment; filename="export.n8np"');
res.setHeader(EXPORT_COUNTS_HEADER, JSON.stringify(counts));
res.setHeader('Access-Control-Expose-Headers', EXPORT_COUNTS_HEADER);
return await new Promise((resolve, reject) => {
stream.on('error', reject);
res.on('finish', () => resolve(res));
res.on('close', () => {
if (!res.writableFinished)
stream.destroy();
resolve(res);
});
stream.pipe(res);
});
}
const n8nPackagesHandlers = {
exportPackage: [
(0, global_middleware_1.publicApiCompositeScope)(PACKAGE_EXPORT_SCOPES),
async (req, res) => {
let workflowIds = [];
let folderIds = [];
let projectIds = [];
let includeVariableValues = true;
try {
const payload = api_types_1.ExportPackageRequestDto.safeParse(req.body);
if (!payload.success) {
throw new bad_request_error_1.BadRequestError(payload.error.errors.map(({ message }) => message).join('; '));
}
workflowIds = payload.data.workflowIds ?? [];
folderIds = payload.data.folderIds ?? [];
projectIds = payload.data.projectIds ?? [];
includeVariableValues = payload.data.includeVariableValues;
if (projectIds.length > 0 && (workflowIds.length > 0 || folderIds.length > 0)) {
throw new bad_request_error_1.BadRequestError('Provide either workflowIds/folderIds or projectIds, not both');
}
if (workflowIds.length === 0 && folderIds.length === 0 && projectIds.length === 0) {
throw new bad_request_error_1.BadRequestError('At least one workflowId, folderId, or projectId is required');
}
const apiKeyScopes = assertPackageExportApiKeyScopes(req, workflowIds, folderIds, projectIds);
const exportResult = await di_1.Container.get(n8n_packages_service_1.N8nPackagesService).exportPackage({
user: req.user,
workflowIds,
folderIds,
projectIds,
includeVariableValues,
canExportVariableValues: apiKeyScopes.includes('variable:list'),
includeTags: payload.data.includeTags,
missingWorkflowDependencyPolicy: payload.data.missingWorkflowDependencyPolicy,
});
return await streamPackageExport(res, exportResult);
}
catch (error) {
di_1.Container.get(event_service_1.EventService).emit('n8n-package-export-failed', {
user: req.user,
reason: (0, package_failure_classifier_1.classifyPackageFailure)(error),
...(workflowIds.length ? { workflowIds } : {}),
...(folderIds.length ? { folderIds } : {}),
...(projectIds.length ? { projectIds } : {}),
});
if (error instanceof package_export_errors_1.PackageEntityAccessDeniedError ||
error instanceof package_export_errors_1.PackageEntityNotFoundError) {
throw new n8n_workflow_1.UserError(error.message, { description: error.description });
}
throw error;
}
},
],
importPackage: [
(0, global_middleware_1.publicApiCompositeScope)('workflow:import'),
async (req, res) => {
let projectId;
let folderId;
try {
const payload = api_types_1.ImportPackageRequestDto.safeParse(req.body ?? {});
if (!payload.success) {
throw new bad_request_error_1.BadRequestError(payload.error.errors.map(({ message }) => message).join('; '));
}
projectId = payload.data.projectId;
folderId = payload.data.folderId;
assertPackageImportApiKeyScopes(req);
const packageFile = (0, import_package_upload_1.resolveImportPackageUpload)(req);
const result = await di_1.Container.get(n8n_packages_service_1.N8nPackagesService).importPackage({
user: req.user,
apiKeyScopes: req.tokenGrant?.apiKeyScopes,
projectId,
folderId,
credentialMatchingMode: payload.data.credentialMatchingMode,
credentialMissingMode: payload.data.credentialMissingMode,
bindings: {
credentials: new Map(Object.entries(payload.data.bindings.credentials ?? {})),
},
workflowConflictPolicy: payload.data.workflowConflictPolicy,
workflowPublishingPolicy: payload.data.workflowPublishingPolicy,
workflowIdPolicy: payload.data.workflowIdPolicy,
missingNodeTypeMode: payload.data.missingNodeTypeMode,
projectConflictPolicy: payload.data.projectConflictPolicy,
folderConflictPolicy: payload.data.folderConflictPolicy,
dataTableMatchingMode: payload.data.dataTableMatchingMode,
dataTableMissingMode: payload.data.dataTableMissingMode,
dataTableSchemaConflictPolicy: payload.data.dataTableSchemaConflictPolicy,
variableMissingMode: payload.data.variableMissingMode,
variableConflictPolicy: payload.data.variableConflictPolicy,
variableParentPolicy: payload.data.variableParentPolicy,
tagMissingMode: payload.data.tagMissingMode,
tagConflictPolicy: payload.data.tagConflictPolicy,
packageBuffer: packageFile.buffer,
});
return res.status(200).json(result);
}
catch (error) {
di_1.Container.get(event_service_1.EventService).emit('n8n-package-import-failed', {
user: req.user,
reason: (0, package_failure_classifier_1.classifyPackageFailure)(error),
...(projectId ? { projectId } : {}),
...(folderId ? { folderId } : {}),
});
throw error;
}
},
],
};
module.exports = n8nPackagesHandlers;
//# sourceMappingURL=n8n-packages.handler.js.map