n8n
Version:
n8n Workflow Automation Tool
160 lines • 7.1 kB
JavaScript
;
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);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.EvalThreadRestoreService = void 0;
const db_1 = require("@n8n/db");
const di_1 = require("@n8n/di");
const n8n_workflow_1 = require("n8n-workflow");
const node_crypto_1 = require("node:crypto");
const bad_request_error_1 = require("../../../errors/response-errors/bad-request.error");
const data_table_service_1 = require("../../../modules/data-table/data-table.service");
function isRecord(value) {
return typeof value === 'object' && value !== null && !Array.isArray(value);
}
function isWorkflowNode(value) {
if (!isRecord(value))
return false;
return (typeof value.id === 'string' &&
typeof value.name === 'string' &&
typeof value.type === 'string' &&
typeof value.typeVersion === 'number' &&
Array.isArray(value.position) &&
value.position.length === 2 &&
value.position.every((coordinate) => typeof coordinate === 'number') &&
isRecord(value.parameters));
}
function isConnections(value) {
return isRecord(value);
}
let EvalThreadRestoreService = class EvalThreadRestoreService {
constructor(workflowRepo, sharedWorkflowRepo, dataTableService) {
this.workflowRepo = workflowRepo;
this.sharedWorkflowRepo = sharedWorkflowRepo;
this.dataTableService = dataTableService;
}
async restoreDataTables(dataTables, projectId, options = {}) {
const uniquifyNames = options.uniquifyNames ?? true;
const idMap = new Map();
try {
for (const table of dataTables) {
if (table.id.length < 8) {
throw new bad_request_error_1.BadRequestError(`Seed data table id "${table.id}" is too short to remap safely (need ≥8 chars)`);
}
let name = table.name;
if (uniquifyNames) {
const suffix = ` [seed ${(0, node_crypto_1.randomUUID)().slice(0, 8)}]`;
name = `${table.name.slice(0, 128 - suffix.length)}${suffix}`;
}
const created = await this.dataTableService.createDataTable(projectId, {
name,
columns: table.columns,
});
idMap.set(table.id, created.id);
if (table.rows && table.rows.length > 0) {
await this.dataTableService.insertRows(created.id, projectId, table.rows);
}
}
}
catch (error) {
await this.deleteDataTables([...idMap.values()], projectId);
throw error;
}
return idMap;
}
async reseedDataTableRows(tableId, projectId, rows) {
await this.dataTableService.clearRows(tableId, projectId);
if (rows.length > 0) {
await this.dataTableService.insertRows(tableId, projectId, rows);
}
}
async deleteDataTables(dataTableIds, projectId) {
for (const id of dataTableIds) {
try {
await this.dataTableService.deleteDataTable(id, projectId);
}
catch {
}
}
}
async restoreWorkflows(workflows, projectId, dataTableIdMap = new Map()) {
const created = [];
try {
for (const workflow of workflows) {
if (await this.createWorkflowPinnedToId(workflow, projectId, dataTableIdMap)) {
created.push(workflow.id);
}
}
}
catch (error) {
await this.deleteWorkflows(created);
throw error;
}
return created;
}
async deleteWorkflows(workflowIds) {
for (const id of workflowIds) {
try {
await this.workflowRepo.delete({ id });
}
catch {
}
}
}
async createWorkflowPinnedToId(workflow, projectId, dataTableIdMap) {
const remapDataTableIds = (value) => {
if (dataTableIdMap.size === 0)
return value;
let serialized = JSON.stringify(value);
for (const [oldId, newId] of dataTableIdMap) {
serialized = serialized.replaceAll(oldId, newId);
}
return (0, n8n_workflow_1.jsonParse)(serialized);
};
const nodes = workflow.nodes.map((node, index) => {
if (!isWorkflowNode(node)) {
throw new bad_request_error_1.BadRequestError(`Seed workflow ${workflow.id} node at index ${index} is not a valid workflow node`);
}
const { credentials: _stripped, ...rest } = node;
const remapped = remapDataTableIds(rest);
if (!isWorkflowNode(remapped)) {
throw new bad_request_error_1.BadRequestError(`Seed workflow ${workflow.id} node at index ${index} became invalid after data-table id remap`);
}
return remapped;
});
const connections = remapDataTableIds(workflow.connections);
if (!isConnections(connections)) {
throw new bad_request_error_1.BadRequestError(`Seed workflow ${workflow.id} connections must be an object`);
}
const owningProject = await this.sharedWorkflowRepo.getWorkflowOwningProject(workflow.id);
if (owningProject && owningProject.id !== projectId) {
throw new bad_request_error_1.BadRequestError(`Seed workflow id ${workflow.id} already exists in another project; refusing to overwrite`);
}
await this.workflowRepo.save(this.workflowRepo.create({
id: workflow.id,
name: workflow.name,
nodes,
connections,
active: false,
versionId: (0, node_crypto_1.randomUUID)(),
}));
if (!owningProject) {
await this.sharedWorkflowRepo.makeOwner([workflow.id], projectId);
return true;
}
return false;
}
};
exports.EvalThreadRestoreService = EvalThreadRestoreService;
exports.EvalThreadRestoreService = EvalThreadRestoreService = __decorate([
(0, di_1.Service)(),
__metadata("design:paramtypes", [db_1.WorkflowRepository, db_1.SharedWorkflowRepository, data_table_service_1.DataTableService])
], EvalThreadRestoreService);
//# sourceMappingURL=thread-restore.service.js.map