UNPKG

@n8n-plus/n8n-plus

Version:

n8n Workflow Automation Tool (plus edition)

194 lines 9.56 kB
"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); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ExecutionPersistence = void 0; const config_1 = require("@n8n/config"); const constants_1 = require("@n8n/constants"); const db_1 = require("@n8n/db"); const di_1 = require("@n8n/di"); const flatted_1 = require("flatted"); const n8n_core_1 = require("n8n-core"); const db_store_1 = require("./execution-data/db-store"); const fs_store_1 = require("./execution-data/fs-store"); const missing_execution_data_error_1 = require("./execution-data/missing-execution-data.error"); const duplicate_execution_error_1 = require("../errors/duplicate-execution.error"); let ExecutionPersistence = class ExecutionPersistence { constructor(executionRepository, binaryDataService, fsStore, dbStore, storageConfig, executionsConfig, databaseConfig) { this.executionRepository = executionRepository; this.binaryDataService = binaryDataService; this.fsStore = fsStore; this.dbStore = dbStore; this.storageConfig = storageConfig; this.executionsConfig = executionsConfig; this.databaseConfig = databaseConfig; } async create(payload) { const { data: rawData, workflowData, ...rest } = payload; const { connections, nodes, name, settings, id } = workflowData; const workflowSnapshot = { connections, nodes, name, settings, id }; const storedAt = this.storageConfig.modeTag; const executionEntity = { ...rest, createdAt: new Date(), storedAt }; const data = (0, flatted_1.stringify)(rawData); const workflowVersionId = workflowData.versionId ?? null; try { return await this.executionRepository.manager.transaction(async (tx) => { const { identifiers } = await tx.insert(db_1.ExecutionEntity, executionEntity); const executionId = String(identifiers[0].id); const ref = { workflowId: id, executionId }; const bundle = { data, workflowData: workflowSnapshot, workflowVersionId }; await this.getStoreFor(storedAt).write(ref, bundle, tx); return executionId; }); } catch (error) { if (executionEntity.deduplicationKey && this.isDuplicateExecutionError(error)) { throw new duplicate_execution_error_1.DuplicateExecutionError(executionEntity.deduplicationKey, error); } throw error; } } async updateExistingExecution(executionId, execution, conditions) { const hasDataField = execution.data !== undefined || execution.workflowData !== undefined; if (!hasDataField) { return await this.updateEntityOnly(executionId, execution, conditions); } const entity = await this.executionRepository.findOne({ where: this.buildEntityWhereCondition(executionId, conditions), select: ['id', 'workflowId', 'storedAt'], }); if (!entity) return false; const ref = { workflowId: entity.workflowId, executionId }; const store = this.getStoreFor(entity.storedAt); return await this.applyDataUpdate(ref, store, execution, conditions); } async deleteInFlightExecution(target) { if (this.executionsConfig.pruneData) { const bufferMs = this.executionsConfig.pruneDataHardDeleteBuffer * constants_1.Time.hours.toMilliseconds; const deletedAt = new Date(Date.now() - bufferMs); await this.executionRepository.update(target.executionId, { deletedAt }); } else { await this.hardDelete(target); } } async hardDelete(target) { const targets = Array.isArray(target) ? target : [target]; if (targets.length === 0) return; const fsTargets = targets.filter((t) => t.storedAt === 'fs'); await Promise.all([ this.executionRepository.deleteByIds(targets.map((t) => t.executionId)), this.binaryDataService.deleteMany(targets.map((t) => ({ type: 'execution', ...t }))), fsTargets.length > 0 ? this.fsStore.delete(fsTargets) : Promise.resolve(), ]); } async hardDeleteBy(criteria) { const refs = await this.executionRepository.deleteExecutionsByFilter(criteria); const fsRefs = refs.filter((r) => r.storedAt === 'fs'); if (fsRefs.length > 0) await this.fsStore.delete(fsRefs); } async updateEntityOnly(executionId, execution, conditions) { const updatableColumns = this.pickUpdatableEntityColumns(execution); if (Object.keys(updatableColumns).length === 0) return true; const whereCondition = this.buildEntityWhereCondition(executionId, conditions); const result = await this.executionRepository.update(whereCondition, updatableColumns); return (result.affected ?? 0) > 0; } async applyDataUpdate(ref, store, execution, conditions) { const { data, workflowData } = execution; const updatableColumns = this.pickUpdatableEntityColumns(execution); return await this.executionRepository.manager.transaction(async (tx) => { const whereCondition = this.buildEntityWhereCondition(ref.executionId, conditions); if (Object.keys(updatableColumns).length > 0) { const result = await tx.update(db_1.ExecutionEntity, whereCondition, updatableColumns); if ((result.affected ?? 0) === 0) return false; } else if (conditions) { const matchingRows = await tx.count(db_1.ExecutionEntity, { where: whereCondition }); if (matchingRows === 0) return false; } const existing = await store.read(ref, tx); if (!existing) throw new missing_execution_data_error_1.MissingExecutionDataError(ref); await store.write(ref, { data: data !== undefined ? (0, flatted_1.stringify)(data) : existing.data, workflowData: workflowData ? this.toWorkflowSnapshot(workflowData) : existing.workflowData, workflowVersionId: existing.workflowVersionId, }, tx); return true; }); } pickUpdatableEntityColumns(execution) { const { id: _id, data: _data, workflowId: _workflowId, workflowData: _workflowData, workflowVersionId: _workflowVersionId, createdAt: _createdAt, startedAt: _startedAt, customData: _customData, ...updatableColumns } = execution; return updatableColumns; } buildEntityWhereCondition(executionId, conditions) { const where = { id: executionId }; if (conditions?.requireStatus) where.status = conditions.requireStatus; if (conditions?.requireNotFinished) where.finished = false; if (conditions?.requireNotCanceled) where.status = (0, db_1.Not)('canceled'); return where; } getStoreFor(location) { switch (location) { case 'db': return this.dbStore; case 'fs': return this.fsStore; } const _exhaustive = location; throw new Error(`Unknown storage location: ${String(_exhaustive)}`); } toWorkflowSnapshot(workflowData) { const { id, name, nodes, connections, settings } = workflowData; return { id, name, nodes, connections, settings }; } isDuplicateExecutionError(error) { if (!(error instanceof Error) || !('driverError' in error)) return false; const { driverError } = error; if (typeof driverError !== 'object' || driverError === null || !('code' in driverError)) { return false; } const { code } = driverError; if (typeof code !== 'string') return false; if (!error.message.includes('deduplicationKey')) return false; if (this.databaseConfig.type === 'postgresdb') { return code === '23505'; } return (code === 'SQLITE_CONSTRAINT_UNIQUE' || (code === 'SQLITE_CONSTRAINT' && error.message.includes('UNIQUE constraint failed'))); } }; exports.ExecutionPersistence = ExecutionPersistence; exports.ExecutionPersistence = ExecutionPersistence = __decorate([ (0, di_1.Service)(), __metadata("design:paramtypes", [db_1.ExecutionRepository, n8n_core_1.BinaryDataService, fs_store_1.FsStore, db_store_1.DbStore, n8n_core_1.StorageConfig, config_1.ExecutionsConfig, config_1.DatabaseConfig]) ], ExecutionPersistence); //# sourceMappingURL=execution-persistence.js.map