UNPKG

n8n

Version:

n8n Workflow Automation Tool

280 lines • 12.9 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.ExternalSecretsProviderConnectionManager = void 0; const backend_common_1 = require("@n8n/backend-common"); const di_1 = require("@n8n/di"); const ensure_error_1 = require("@n8n/utils/errors/ensure-error"); const n8n_workflow_1 = require("n8n-workflow"); const provider_lifecycle_service_1 = require("./provider-lifecycle.service"); const provider_registry_service_1 = require("./provider-registry.service"); const retry_manager_service_1 = require("./retry-manager.service"); const secrets_cache_service_1 = require("./secrets-cache.service"); const COMPLETED_CONNECTION = { completion: Promise.resolve(), }; let ExternalSecretsProviderConnectionManager = class ExternalSecretsProviderConnectionManager { constructor(logger, providerRegistry, providerLifecycle, retryManager, secretsCache) { this.logger = logger; this.providerRegistry = providerRegistry; this.providerLifecycle = providerLifecycle; this.retryManager = retryManager; this.secretsCache = secretsCache; this.replacementCandidates = new Map(); this.logger = this.logger.scoped('external-secrets'); } async upsertProviderConnection(input) { const { completion } = await this.prepareProviderConnection(input); await completion; } async upsertProviderConnections(inputs) { const completions = []; for (const input of inputs) { const { completion } = await this.prepareProviderConnection(input); completions.push(completion); } await Promise.all(completions); } shutdown() { this.replacementCandidates.clear(); this.retryManager.cancelAll(); void this.providerRegistry.disconnectAll(); } async prepareProviderConnection({ providerKey, providerType, config, }) { if (this.providerRegistry.has(providerKey) || this.replacementCandidates.has(providerKey)) { await this.replaceProviderConnection(providerKey, providerType, config); return COMPLETED_CONNECTION; } return await this.addProviderConnection(providerKey, providerType, config); } async removeProviderConnection(providerKey) { this.invalidateReplacement(providerKey); this.retryManager.cancelRetry(providerKey); this.logger.debug('Removing external secrets provider connection', { providerKey, }); const existingProvider = this.providerRegistry.get(providerKey); this.providerRegistry.remove(providerKey); if (existingProvider) { await this.providerLifecycle.disconnect(existingProvider); } } async connectProviderWithRetry(providerKey) { return await this.retryManager.runWithRetry(providerKey, async () => await this.connectProvider(providerKey)); } async disconnectProvider(providerKey) { this.invalidateReplacement(providerKey); this.retryManager.cancelRetry(providerKey); const provider = this.providerRegistry.get(providerKey); this.providerRegistry.remove(providerKey); if (provider) { await this.providerLifecycle.disconnect(provider); } } async addProviderConnection(providerKey, providerType, config) { this.logger.debug('Adding external secrets provider connection', { providerKey, providerType, }); const result = await this.providerLifecycle.initialize(providerType, config); if (!result.success || !result.provider) { this.logger.error('Failed to initialize external secrets provider connection', { providerKey, providerType, error: result.error, }); return COMPLETED_CONNECTION; } this.providerRegistry.set(providerKey, result.provider); if (config.connected) { await this.connectProviderWithRetry(providerKey); } return { completion: this.secretsCache.refreshProvider(providerKey, result.provider), }; } async replaceProviderConnection(providerKey, providerType, config) { const candidate = this.beginReplacement(providerKey, providerType); const initResult = await this.providerLifecycle.initialize(providerType, config); candidate.provider = initResult.provider; if (!initResult.success || !initResult.provider) { await this.settleReplacement(candidate, { kind: 'failed', phase: 'initialization', provider: initResult.provider, error: initResult.error ?? new n8n_workflow_1.UnexpectedError(`Failed to initialize provider ${providerKey}`), }); return; } if (!config.connected) { await this.settleReplacement(candidate, { kind: 'ready', phase: 'disconnected', provider: initResult.provider, }); return; } if (!this.isCurrentReplacement(candidate)) { await this.disposeSupersededCandidate(candidate, 'initialization'); return; } await this.retryManager.runWithRetry(providerKey, async () => await this.connectReplacement(candidate)); } beginReplacement(providerKey, providerType) { const candidate = { providerKey, providerType }; this.replacementCandidates.set(providerKey, candidate); this.retryManager.cancelRetry(providerKey); this.logger.debug('External secrets provider replacement started', { providerKey, providerType, phase: 'initialization', }); return candidate; } async connectReplacement(candidate) { const provider = candidate.provider; if (!provider) { return { success: true }; } if (this.providerRegistry.get(candidate.providerKey) === provider) { this.providerRegistry.remove(candidate.providerKey); } const connectResult = await this.providerLifecycle.connect(provider); if (connectResult.success) { this.startReplacementHydration(candidate, provider); return { success: true }; } const published = await this.publishRetryableConnectionFailure(candidate, connectResult.error ?? new n8n_workflow_1.UnexpectedError(`Failed to connect provider ${candidate.providerKey}`)); return published ? connectResult : { success: true }; } startReplacementHydration(candidate, provider) { void this.runReplacementHydration(candidate, provider).catch((error) => { this.logger.error('Unexpected error settling external secrets provider replacement', { providerKey: candidate.providerKey, providerType: candidate.providerType, phase: 'hydration-settlement', error: (0, ensure_error_1.ensureError)(error), }); }); } async runReplacementHydration(candidate, provider) { try { await provider.update(); } catch (error) { await this.settleReplacement(candidate, { kind: 'failed', phase: 'hydration', provider, error: (0, ensure_error_1.ensureError)(error), }); return; } await this.settleReplacement(candidate, { kind: 'ready', phase: 'hydration', provider, }); } async settleReplacement(candidate, outcome) { if (!this.isCurrentReplacement(candidate)) { await this.disposeSupersededCandidate(candidate, outcome.phase); return; } const existingProvider = this.providerRegistry.get(candidate.providerKey); if (outcome.kind === 'ready') { this.providerRegistry.set(candidate.providerKey, outcome.provider); this.logger.debug('External secrets provider replacement activated', { providerKey: candidate.providerKey, providerType: candidate.providerType, phase: outcome.phase, }); } else { outcome.provider?.setState('error', outcome.error); if (outcome.provider) { this.providerRegistry.set(candidate.providerKey, outcome.provider); } else { this.providerRegistry.remove(candidate.providerKey); } this.logger.error('External secrets provider replacement reached terminal failure', { providerKey: candidate.providerKey, providerType: candidate.providerType, phase: outcome.phase, error: outcome.error, }); } this.replacementCandidates.delete(candidate.providerKey); if (existingProvider && existingProvider !== outcome.provider) { await this.providerLifecycle.disconnect(existingProvider); } } async publishRetryableConnectionFailure(candidate, error) { if (!this.isCurrentReplacement(candidate) || !candidate.provider) { await this.disposeSupersededCandidate(candidate, 'connection'); return false; } candidate.provider.setState('error', error); this.logger.error('External secrets provider replacement connection attempt failed', { providerKey: candidate.providerKey, providerType: candidate.providerType, phase: 'connection', error, }); const existingProvider = this.providerRegistry.get(candidate.providerKey); const hasConnectedPredecessor = existingProvider !== undefined && existingProvider !== candidate.provider && existingProvider.state === 'connected'; if (hasConnectedPredecessor) { return true; } this.providerRegistry.set(candidate.providerKey, candidate.provider); if (existingProvider && existingProvider !== candidate.provider) { await this.providerLifecycle.disconnect(existingProvider); } return true; } async disposeSupersededCandidate(candidate, phase) { const { provider, providerKey, providerType } = candidate; const shouldDispose = provider !== undefined && this.providerRegistry.get(providerKey) !== provider; this.logger.debug('External secrets provider superseded replacement discarded', { providerKey, providerType, phase, disposed: shouldDispose, }); if (shouldDispose) { await this.providerLifecycle.disconnect(provider); } } isCurrentReplacement(candidate) { return this.replacementCandidates.get(candidate.providerKey) === candidate; } invalidateReplacement(providerKey) { this.replacementCandidates.delete(providerKey); } async connectProvider(providerKey) { const provider = this.providerRegistry.get(providerKey); if (!provider) { this.logger.warn(`Cannot connect provider ${providerKey}: not found in registry`); throw new Error(`Provider ${providerKey} not found in registry`); } return await this.providerLifecycle.connect(provider); } }; exports.ExternalSecretsProviderConnectionManager = ExternalSecretsProviderConnectionManager; exports.ExternalSecretsProviderConnectionManager = ExternalSecretsProviderConnectionManager = __decorate([ (0, di_1.Service)(), __metadata("design:paramtypes", [backend_common_1.Logger, provider_registry_service_1.ExternalSecretsProviderRegistry, provider_lifecycle_service_1.ExternalSecretsProviderLifecycle, retry_manager_service_1.ExternalSecretsRetryManager, secrets_cache_service_1.ExternalSecretsSecretsCache]) ], ExternalSecretsProviderConnectionManager); //# sourceMappingURL=external-secrets-provider-connection-manager.ee.js.map