@tamyla/clodo-framework 3.1.21 → 3.1.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/README.md +53 -0
- package/dist/bin/clodo-service.js +47 -15
- package/dist/bin/commands/deploy.js +115 -83
- package/dist/bin/commands/helpers/deployment-ui.js +138 -0
- package/dist/bin/commands/helpers/deployment-verification.js +251 -0
- package/dist/bin/commands/helpers/error-recovery.js +80 -0
- package/dist/bin/commands/helpers/resource-detection.js +113 -0
- package/dist/bin/commands/validate.js +1 -1
- package/dist/bin/security/security-cli.js +1 -1
- package/dist/bin/shared/cache/configuration-cache.js +82 -0
- package/dist/bin/shared/cloudflare/domain-manager.js +1 -1
- package/dist/bin/shared/cloudflare/index.js +1 -1
- package/dist/bin/shared/cloudflare/ops.js +6 -4
- package/dist/bin/shared/config/ConfigurationManager.js +23 -1
- package/dist/bin/shared/config/command-config-manager.js +19 -3
- package/dist/bin/shared/config/index.js +1 -1
- package/dist/bin/shared/deployment/credential-collector.js +30 -7
- package/dist/bin/shared/deployment/index.js +2 -2
- package/dist/bin/shared/deployment/rollback-manager.js +4 -520
- package/dist/bin/shared/deployment/utilities/d1-error-recovery.js +177 -0
- package/dist/bin/shared/deployment/validator.js +40 -10
- package/dist/bin/shared/deployment/workflows/deployment-summary.js +214 -0
- package/dist/bin/shared/deployment/workflows/interactive-confirmation.js +188 -0
- package/dist/bin/shared/deployment/workflows/interactive-database-workflow.js +234 -0
- package/dist/bin/shared/deployment/workflows/interactive-domain-info-gatherer.js +240 -0
- package/dist/bin/shared/deployment/workflows/interactive-secret-workflow.js +228 -0
- package/dist/bin/shared/deployment/workflows/interactive-testing-workflow.js +235 -0
- package/dist/bin/shared/deployment/workflows/interactive-validation.js +218 -0
- package/dist/bin/shared/error-handling/error-classifier.js +46 -0
- package/dist/bin/shared/monitoring/health-checker.js +129 -1
- package/dist/bin/shared/monitoring/memory-manager.js +17 -6
- package/dist/bin/shared/routing/domain-router.js +1 -1
- package/dist/bin/shared/utils/deployment-validator.js +97 -0
- package/dist/bin/shared/utils/formatters.js +10 -0
- package/dist/bin/shared/utils/index.js +13 -1
- package/dist/bin/shared/utils/interactive-prompts.js +34 -18
- package/dist/bin/shared/utils/progress-manager.js +2 -2
- package/dist/bin/shared/utils/progress-spinner.js +53 -0
- package/dist/bin/shared/utils/sensitive-redactor.js +91 -0
- package/dist/bin/shared/validation/ValidationRegistry.js +1 -1
- package/dist/security/index.js +1 -1
- package/dist/security/patterns/insecure-patterns.js +1 -1
- package/dist/utils/constants.js +102 -0
- package/dist/utils/deployment/wrangler-config-manager.js +215 -48
- package/dist/utils/framework-config.js +2 -2
- package/dist/utils/interactive-prompts.js +10 -59
- package/package.json +16 -8
- package/dist/bin/clodo-service-old.js +0 -868
- package/dist/bin/clodo-service-test.js +0 -10
- package/dist/bin/commands/assess.js +0 -91
- package/dist/bin/commands/create.js +0 -77
- package/dist/bin/commands/diagnose.js +0 -83
- package/dist/bin/commands/helpers.js +0 -138
- package/dist/bin/commands/update.js +0 -75
- package/dist/bin/database/deployment-db-manager.js +0 -423
- package/dist/bin/database/enterprise-db-manager.js +0 -457
- package/dist/bin/database/wrangler-d1-manager.js +0 -685
- package/dist/bin/deployment/enterprise-deploy.js +0 -877
- package/dist/bin/deployment/master-deploy.js +0 -1376
- package/dist/bin/deployment/modular-enterprise-deploy.js +0 -466
- package/dist/bin/deployment/modules/DeploymentConfiguration.js +0 -395
- package/dist/bin/deployment/modules/DeploymentOrchestrator.js +0 -492
- package/dist/bin/deployment/modules/EnvironmentManager.js +0 -517
- package/dist/bin/deployment/modules/MonitoringIntegration.js +0 -560
- package/dist/bin/deployment/modules/ValidationManager.js +0 -342
- package/dist/bin/deployment/orchestration/BaseDeploymentOrchestrator.js +0 -426
- package/dist/bin/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
- package/dist/bin/deployment/orchestration/PortfolioOrchestrator.js +0 -273
- package/dist/bin/deployment/orchestration/SingleServiceOrchestrator.js +0 -231
- package/dist/bin/deployment/orchestration/UnifiedDeploymentOrchestrator.js +0 -662
- package/dist/bin/deployment/test-interactive-utils.js +0 -66
- package/dist/bin/portfolio/portfolio-manager.js +0 -487
- package/dist/bin/service-management/create-service.js +0 -122
- package/dist/bin/service-management/init-service.js +0 -79
- package/dist/config/customers.js +0 -623
- package/dist/config/domains.js +0 -186
- package/dist/config/index.js +0 -6
- package/dist/database/database-orchestrator.js +0 -795
- package/dist/database/index.js +0 -4
- package/dist/deployment/index.js +0 -11
- package/dist/deployment/orchestration/BaseDeploymentOrchestrator.js +0 -426
- package/dist/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
- package/dist/deployment/orchestration/PortfolioOrchestrator.js +0 -273
- package/dist/deployment/orchestration/SingleServiceOrchestrator.js +0 -231
- package/dist/deployment/orchestration/UnifiedDeploymentOrchestrator.js +0 -662
- package/dist/deployment/orchestration/index.js +0 -17
- package/dist/deployment/rollback-manager.js +0 -36
- package/dist/deployment/wrangler-deployer.js +0 -640
- package/dist/handlers/GenericRouteHandler.js +0 -532
- package/dist/migration/MigrationAdapters.js +0 -562
- package/dist/modules/ModuleManager.js +0 -668
- package/dist/modules/security.js +0 -96
- package/dist/orchestration/cross-domain-coordinator.js +0 -1083
- package/dist/orchestration/index.js +0 -5
- package/dist/orchestration/modules/DeploymentCoordinator.js +0 -368
- package/dist/orchestration/modules/DomainResolver.js +0 -198
- package/dist/orchestration/modules/StateManager.js +0 -332
- package/dist/orchestration/multi-domain-orchestrator.js +0 -724
- package/dist/routing/EnhancedRouter.js +0 -158
- package/dist/schema/SchemaManager.js +0 -778
- package/dist/service-management/ConfirmationEngine.js +0 -412
- package/dist/service-management/ErrorTracker.js +0 -299
- package/dist/service-management/GenerationEngine.js +0 -447
- package/dist/service-management/InputCollector.js +0 -619
- package/dist/service-management/ServiceCreator.js +0 -265
- package/dist/service-management/ServiceInitializer.js +0 -453
- package/dist/service-management/ServiceOrchestrator.js +0 -633
- package/dist/service-management/generators/BaseGenerator.js +0 -233
- package/dist/service-management/generators/GeneratorRegistry.js +0 -254
- package/dist/service-management/generators/cicd/CiWorkflowGenerator.js +0 -87
- package/dist/service-management/generators/cicd/DeployWorkflowGenerator.js +0 -106
- package/dist/service-management/generators/code/ServiceHandlersGenerator.js +0 -235
- package/dist/service-management/generators/code/ServiceMiddlewareGenerator.js +0 -116
- package/dist/service-management/generators/code/ServiceUtilsGenerator.js +0 -246
- package/dist/service-management/generators/code/WorkerIndexGenerator.js +0 -143
- package/dist/service-management/generators/config/DevelopmentEnvGenerator.js +0 -101
- package/dist/service-management/generators/config/DomainsConfigGenerator.js +0 -175
- package/dist/service-management/generators/config/EnvExampleGenerator.js +0 -178
- package/dist/service-management/generators/config/ProductionEnvGenerator.js +0 -97
- package/dist/service-management/generators/config/StagingEnvGenerator.js +0 -97
- package/dist/service-management/generators/config/WranglerTomlGenerator.js +0 -238
- package/dist/service-management/generators/core/PackageJsonGenerator.js +0 -243
- package/dist/service-management/generators/core/SiteConfigGenerator.js +0 -115
- package/dist/service-management/generators/documentation/ApiDocsGenerator.js +0 -331
- package/dist/service-management/generators/documentation/ConfigurationDocsGenerator.js +0 -294
- package/dist/service-management/generators/documentation/DeploymentDocsGenerator.js +0 -244
- package/dist/service-management/generators/documentation/ReadmeGenerator.js +0 -196
- package/dist/service-management/generators/schemas/ServiceSchemaGenerator.js +0 -190
- package/dist/service-management/generators/scripts/DeployScriptGenerator.js +0 -123
- package/dist/service-management/generators/scripts/HealthCheckScriptGenerator.js +0 -101
- package/dist/service-management/generators/scripts/SetupScriptGenerator.js +0 -88
- package/dist/service-management/generators/service-types/StaticSiteGenerator.js +0 -342
- package/dist/service-management/generators/testing/EslintConfigGenerator.js +0 -85
- package/dist/service-management/generators/testing/IntegrationTestsGenerator.js +0 -237
- package/dist/service-management/generators/testing/JestConfigGenerator.js +0 -72
- package/dist/service-management/generators/testing/UnitTestsGenerator.js +0 -277
- package/dist/service-management/generators/tooling/DockerComposeGenerator.js +0 -71
- package/dist/service-management/generators/tooling/GitignoreGenerator.js +0 -143
- package/dist/service-management/generators/utils/FileWriter.js +0 -179
- package/dist/service-management/generators/utils/PathResolver.js +0 -157
- package/dist/service-management/generators/utils/ServiceManifestGenerator.js +0 -111
- package/dist/service-management/generators/utils/TemplateEngine.js +0 -185
- package/dist/service-management/generators/utils/index.js +0 -18
- package/dist/service-management/handlers/ConfirmationHandler.js +0 -71
- package/dist/service-management/handlers/GenerationHandler.js +0 -80
- package/dist/service-management/handlers/InputHandler.js +0 -59
- package/dist/service-management/handlers/ValidationHandler.js +0 -203
- package/dist/service-management/index.js +0 -14
- package/dist/service-management/routing/DomainRouteMapper.js +0 -311
- package/dist/service-management/routing/RouteGenerator.js +0 -266
- package/dist/service-management/routing/WranglerRoutesBuilder.js +0 -273
- package/dist/service-management/routing/index.js +0 -14
- package/dist/service-management/services/DirectoryStructureService.js +0 -56
- package/dist/service-management/services/GenerationCoordinator.js +0 -208
- package/dist/service-management/services/GeneratorRegistry.js +0 -174
- package/dist/services/GenericDataService.js +0 -501
- package/dist/ui-structures/concepts/second-order-acquisition-strategy.md +0 -286
- package/dist/ui-structures/concepts/service-lifecycle-management.md +0 -150
- package/dist/ui-structures/concepts/service-manifest-guide.md +0 -309
- package/dist/ui-structures/concepts/three-tier-categorization-strategy.md +0 -231
- package/dist/ui-structures/creation/automated-generation-ui.json +0 -246
- package/dist/ui-structures/creation/core-inputs-ui.json +0 -217
- package/dist/ui-structures/creation/smart-confirmable-ui.json +0 -451
- package/dist/ui-structures/reference/absolutely-required-inputs.json +0 -315
- package/dist/ui-structures/reference/service-manifest-template.json +0 -342
- package/dist/version/VersionDetector.js +0 -723
- package/dist/worker/index.js +0 -4
- package/dist/worker/integration.js +0 -351
|
@@ -1,724 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Multi-Domain Orchestrator Module
|
|
5
|
-
* Enterprise-grade deployment orchestration with state management,
|
|
6
|
-
* rollback capabilities, and portfolio-wide coordination
|
|
7
|
-
*
|
|
8
|
-
* Now uses modular architecture for improved maintainability and testability
|
|
9
|
-
*/
|
|
10
|
-
import { DomainResolver } from './modules/DomainResolver.js';
|
|
11
|
-
import { DeploymentCoordinator } from './modules/DeploymentCoordinator.js';
|
|
12
|
-
import { StateManager } from './modules/StateManager.js';
|
|
13
|
-
import { DatabaseOrchestrator } from '../database/database-orchestrator.js';
|
|
14
|
-
import { EnhancedSecretManager } from '../utils/deployment/secret-generator.js';
|
|
15
|
-
import { WranglerConfigManager } from '../utils/deployment/wrangler-config-manager.js';
|
|
16
|
-
import { ConfigurationValidator } from '../security/ConfigurationValidator.js';
|
|
17
|
-
import { exec } from 'child_process';
|
|
18
|
-
import { promisify } from 'util';
|
|
19
|
-
import { join } from 'path';
|
|
20
|
-
import { createDatabase, databaseExists, getDatabaseId } from '../utils/cloudflare/index.js';
|
|
21
|
-
const execAsync = promisify(exec);
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Multi-Domain Deployment Orchestrator
|
|
25
|
-
* Manages enterprise-level deployments across multiple domains with comprehensive state tracking
|
|
26
|
-
*
|
|
27
|
-
* REFACTORED: Now uses modular components for domain resolution, deployment coordination, and state management
|
|
28
|
-
*/
|
|
29
|
-
export class MultiDomainOrchestrator {
|
|
30
|
-
constructor(options = {}) {
|
|
31
|
-
this.domains = options.domains || [];
|
|
32
|
-
this.environment = options.environment || 'production';
|
|
33
|
-
this.dryRun = options.dryRun || false;
|
|
34
|
-
this.skipTests = options.skipTests || false;
|
|
35
|
-
this.parallelDeployments = options.parallelDeployments || 3;
|
|
36
|
-
this.servicePath = options.servicePath || process.cwd();
|
|
37
|
-
|
|
38
|
-
// Cloudflare credentials for API-based operations
|
|
39
|
-
this.cloudflareToken = options.cloudflareToken;
|
|
40
|
-
this.cloudflareAccountId = options.cloudflareAccountId;
|
|
41
|
-
|
|
42
|
-
// Configure wrangler to use API token when available
|
|
43
|
-
// This ensures all wrangler operations use the same account as API operations
|
|
44
|
-
if (this.cloudflareToken) {
|
|
45
|
-
process.env.CLOUDFLARE_API_TOKEN = this.cloudflareToken;
|
|
46
|
-
console.log(`🔑 Configured wrangler to use API token authentication`);
|
|
47
|
-
}
|
|
48
|
-
if (this.cloudflareAccountId) {
|
|
49
|
-
process.env.CLOUDFLARE_ACCOUNT_ID = this.cloudflareAccountId;
|
|
50
|
-
console.log(`🔑 Configured wrangler to use account ID: ${this.cloudflareAccountId}`);
|
|
51
|
-
}
|
|
52
|
-
if (this.cloudflareAccountId) {
|
|
53
|
-
process.env.CLOUDFLARE_ACCOUNT_ID = this.cloudflareAccountId;
|
|
54
|
-
console.log(`🔑 Configured wrangler to use account ID: ${this.cloudflareAccountId}`);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Initialize modular components
|
|
58
|
-
this.domainResolver = new DomainResolver({
|
|
59
|
-
environment: this.environment,
|
|
60
|
-
validationLevel: options.validationLevel || 'basic',
|
|
61
|
-
cacheEnabled: options.cacheEnabled !== false
|
|
62
|
-
});
|
|
63
|
-
this.deploymentCoordinator = new DeploymentCoordinator({
|
|
64
|
-
parallelDeployments: this.parallelDeployments,
|
|
65
|
-
skipTests: this.skipTests,
|
|
66
|
-
dryRun: this.dryRun,
|
|
67
|
-
environment: this.environment,
|
|
68
|
-
batchPauseMs: options.batchPauseMs || 2000
|
|
69
|
-
});
|
|
70
|
-
this.stateManager = new StateManager({
|
|
71
|
-
environment: this.environment,
|
|
72
|
-
dryRun: this.dryRun,
|
|
73
|
-
domains: this.domains,
|
|
74
|
-
enablePersistence: options.enablePersistence !== false,
|
|
75
|
-
rollbackEnabled: options.rollbackEnabled !== false
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
// Initialize enterprise-grade utilities
|
|
79
|
-
this.databaseOrchestrator = new DatabaseOrchestrator({
|
|
80
|
-
projectRoot: this.servicePath,
|
|
81
|
-
dryRun: this.dryRun,
|
|
82
|
-
cloudflareToken: this.cloudflareToken,
|
|
83
|
-
cloudflareAccountId: this.cloudflareAccountId
|
|
84
|
-
});
|
|
85
|
-
this.secretManager = new EnhancedSecretManager({
|
|
86
|
-
projectRoot: this.servicePath,
|
|
87
|
-
dryRun: this.dryRun
|
|
88
|
-
});
|
|
89
|
-
this.wranglerConfigManager = new WranglerConfigManager({
|
|
90
|
-
projectRoot: this.servicePath,
|
|
91
|
-
dryRun: this.dryRun,
|
|
92
|
-
verbose: options.verbose || false,
|
|
93
|
-
accountId: this.cloudflareAccountId
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
// ConfigurationValidator is a static class - don't instantiate
|
|
97
|
-
// Access via ConfigurationValidator.validate() directly
|
|
98
|
-
|
|
99
|
-
// Legacy compatibility: expose portfolioState for backward compatibility
|
|
100
|
-
this.portfolioState = this.stateManager.portfolioState;
|
|
101
|
-
|
|
102
|
-
// Note: Async initialization required - call initialize() after construction
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Initialize the orchestrator asynchronously
|
|
107
|
-
* Uses modular components for domain resolution and state initialization
|
|
108
|
-
*/
|
|
109
|
-
async initialize() {
|
|
110
|
-
console.log('🌐 Multi-Domain Orchestrator v2.0 (Modular)');
|
|
111
|
-
console.log('===========================================');
|
|
112
|
-
console.log(`📊 Portfolio: ${this.domains.length} domains`);
|
|
113
|
-
console.log(`🌍 Environment: ${this.environment}`);
|
|
114
|
-
console.log(`🆔 Orchestration ID: ${this.portfolioState.orchestrationId}`);
|
|
115
|
-
console.log(`🔍 Mode: ${this.dryRun ? 'DRY RUN' : 'LIVE DEPLOYMENT'}`);
|
|
116
|
-
console.log(`⚡ Parallel Deployments: ${this.parallelDeployments}`);
|
|
117
|
-
console.log('🧩 Modular Components: DomainResolver, DeploymentCoordinator, StateManager');
|
|
118
|
-
console.log('');
|
|
119
|
-
|
|
120
|
-
// Initialize all modular components
|
|
121
|
-
await this.stateManager.initializeDomainStates(this.domains);
|
|
122
|
-
|
|
123
|
-
// Pre-resolve all domain configurations if domains are specified
|
|
124
|
-
if (this.domains.length > 0) {
|
|
125
|
-
const configs = await this.domainResolver.resolveMultipleDomains(this.domains);
|
|
126
|
-
|
|
127
|
-
// Update domain states with resolved configurations
|
|
128
|
-
for (const [domain, config] of Object.entries(configs)) {
|
|
129
|
-
const domainState = this.portfolioState.domainStates.get(domain);
|
|
130
|
-
if (domainState) {
|
|
131
|
-
domainState.config = config;
|
|
132
|
-
this.stateManager.updateDomainState(domain, {
|
|
133
|
-
config
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
await this.stateManager.logAuditEvent('orchestrator_initialized', {
|
|
139
|
-
domains: this.domains,
|
|
140
|
-
environment: this.environment,
|
|
141
|
-
modularComponents: ['DomainResolver', 'DeploymentCoordinator', 'StateManager']
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Legacy method for backward compatibility
|
|
147
|
-
* @deprecated Use stateManager.generateOrchestrationId() instead
|
|
148
|
-
*/
|
|
149
|
-
generateOrchestrationId() {
|
|
150
|
-
return this.stateManager.generateOrchestrationId();
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Legacy method for backward compatibility
|
|
155
|
-
* @deprecated Use stateManager.generateDeploymentId() instead
|
|
156
|
-
*/
|
|
157
|
-
generateDeploymentId(domain) {
|
|
158
|
-
return this.stateManager.generateDeploymentId(domain);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Legacy method for backward compatibility
|
|
163
|
-
* @deprecated Use domainResolver.generateDomainConfig() instead
|
|
164
|
-
*/
|
|
165
|
-
generateDomainConfig(domain) {
|
|
166
|
-
return this.domainResolver.generateDomainConfig(domain);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Deploy to single domain using modular deployment coordinator
|
|
171
|
-
* @param {string} domain - Domain to deploy
|
|
172
|
-
* @param {Object} deploymentOptions - Deployment configuration options
|
|
173
|
-
* @returns {Promise<Object>} Deployment result
|
|
174
|
-
*/
|
|
175
|
-
async deploySingleDomain(domain, deploymentOptions = {}) {
|
|
176
|
-
const domainState = this.portfolioState.domainStates.get(domain);
|
|
177
|
-
if (!domainState) {
|
|
178
|
-
throw new Error(`Domain ${domain} not found in portfolio`);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Store deployment options in domain state for handlers to access
|
|
182
|
-
domainState.deploymentOptions = deploymentOptions;
|
|
183
|
-
|
|
184
|
-
// Create handlers that delegate to our legacy methods for backward compatibility
|
|
185
|
-
const handlers = {
|
|
186
|
-
validation: d => this.validateDomainPrerequisites(d),
|
|
187
|
-
initialization: d => this.initializeDomainDeployment(d),
|
|
188
|
-
database: d => this.setupDomainDatabase(d),
|
|
189
|
-
secrets: d => this.handleDomainSecrets(d),
|
|
190
|
-
deployment: d => this.deployDomainWorker(d),
|
|
191
|
-
'post-validation': d => this.validateDomainDeployment(d)
|
|
192
|
-
};
|
|
193
|
-
return await this.deploymentCoordinator.deploySingleDomain(domain, domainState, handlers);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Deploy to multiple domains using modular deployment coordinator
|
|
198
|
-
* @returns {Promise<Object>} Portfolio deployment results
|
|
199
|
-
*/
|
|
200
|
-
async deployPortfolio() {
|
|
201
|
-
// Create handlers that delegate to our legacy methods for backward compatibility
|
|
202
|
-
const handlers = {
|
|
203
|
-
validation: d => this.validateDomainPrerequisites(d),
|
|
204
|
-
initialization: d => this.initializeDomainDeployment(d),
|
|
205
|
-
database: d => this.setupDomainDatabase(d),
|
|
206
|
-
secrets: d => this.handleDomainSecrets(d),
|
|
207
|
-
deployment: d => this.deployDomainWorker(d),
|
|
208
|
-
'post-validation': d => this.validateDomainDeployment(d)
|
|
209
|
-
};
|
|
210
|
-
return await this.deploymentCoordinator.deployPortfolio(this.domains, this.portfolioState.domainStates, handlers);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Legacy method for backward compatibility
|
|
215
|
-
* @deprecated Use deploymentCoordinator.createDeploymentBatches() instead
|
|
216
|
-
*/
|
|
217
|
-
createDeploymentBatches() {
|
|
218
|
-
return this.deploymentCoordinator.createDeploymentBatches(this.domains);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Legacy method for backward compatibility
|
|
223
|
-
* @deprecated Use stateManager.logAuditEvent() instead
|
|
224
|
-
*/
|
|
225
|
-
logAuditEvent(event, domain, details = {}) {
|
|
226
|
-
return this.stateManager.logAuditEvent(event, domain, details);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Legacy method for backward compatibility
|
|
231
|
-
* @deprecated Use stateManager.saveAuditLog() instead
|
|
232
|
-
*/
|
|
233
|
-
async saveAuditLog() {
|
|
234
|
-
return await this.stateManager.saveAuditLog();
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* Legacy method for backward compatibility
|
|
239
|
-
* @deprecated Use domainResolver.validateDomainPrerequisites() instead
|
|
240
|
-
*/
|
|
241
|
-
async validateDomainPrerequisites(domain) {
|
|
242
|
-
return await this.domainResolver.validateDomainPrerequisites(domain);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Initialize domain deployment with security validation
|
|
247
|
-
*/
|
|
248
|
-
async initializeDomainDeployment(domain) {
|
|
249
|
-
console.log(` 🔧 Initializing deployment for ${domain}`);
|
|
250
|
-
|
|
251
|
-
// Validate domain configuration using ConfigurationValidator
|
|
252
|
-
try {
|
|
253
|
-
const domainState = this.portfolioState.domainStates.get(domain);
|
|
254
|
-
const config = domainState?.config || {};
|
|
255
|
-
|
|
256
|
-
// Perform security validation using static method
|
|
257
|
-
const validationIssues = ConfigurationValidator.validate(config, this.environment);
|
|
258
|
-
if (validationIssues.length > 0) {
|
|
259
|
-
console.log(` ⚠️ Found ${validationIssues.length} configuration warnings:`);
|
|
260
|
-
validationIssues.forEach(issue => {
|
|
261
|
-
console.log(` • ${issue}`);
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
// Don't block deployment for warnings, just log them
|
|
265
|
-
this.stateManager.logAuditEvent('VALIDATION_WARNINGS', domain, {
|
|
266
|
-
issues: validationIssues,
|
|
267
|
-
environment: this.environment
|
|
268
|
-
});
|
|
269
|
-
} else {
|
|
270
|
-
console.log(` ✅ Configuration validated successfully`);
|
|
271
|
-
}
|
|
272
|
-
return true;
|
|
273
|
-
} catch (error) {
|
|
274
|
-
console.error(` ❌ Initialization failed: ${error.message}`);
|
|
275
|
-
throw error;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* Setup domain database using DatabaseOrchestrator
|
|
281
|
-
*/
|
|
282
|
-
async setupDomainDatabase(domain) {
|
|
283
|
-
console.log(` 🗄️ Setting up database for ${domain}`);
|
|
284
|
-
if (this.dryRun) {
|
|
285
|
-
console.log(` � DRY RUN: Would create database for ${domain}`);
|
|
286
|
-
const databaseName = `${domain.replace(/\./g, '-')}-${this.environment}-db`;
|
|
287
|
-
return {
|
|
288
|
-
databaseName,
|
|
289
|
-
databaseId: 'dry-run-id',
|
|
290
|
-
created: false
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
|
-
try {
|
|
294
|
-
// Create D1 database using Cloudflare ops
|
|
295
|
-
const databaseName = `${domain.replace(/\./g, '-')}-${this.environment}-db`;
|
|
296
|
-
|
|
297
|
-
// Check if database already exists
|
|
298
|
-
console.log(` � Checking if database exists: ${databaseName}`);
|
|
299
|
-
let exists,
|
|
300
|
-
databaseId,
|
|
301
|
-
created = false;
|
|
302
|
-
|
|
303
|
-
// Use API-based operations if credentials are available
|
|
304
|
-
if (this.cloudflareToken && this.cloudflareAccountId) {
|
|
305
|
-
console.log(` 🔑 Using API token authentication for account: ${this.cloudflareAccountId}`);
|
|
306
|
-
try {
|
|
307
|
-
exists = await databaseExists(databaseName, {
|
|
308
|
-
apiToken: this.cloudflareToken,
|
|
309
|
-
accountId: this.cloudflareAccountId
|
|
310
|
-
});
|
|
311
|
-
if (exists) {
|
|
312
|
-
console.log(` ✅ Database already exists: ${databaseName}`);
|
|
313
|
-
databaseId = await getDatabaseId(databaseName, {
|
|
314
|
-
apiToken: this.cloudflareToken,
|
|
315
|
-
accountId: this.cloudflareAccountId
|
|
316
|
-
});
|
|
317
|
-
console.log(` 📊 Existing Database ID: ${databaseId}`);
|
|
318
|
-
} else {
|
|
319
|
-
console.log(` 📦 Creating database: ${databaseName}`);
|
|
320
|
-
databaseId = await createDatabase(databaseName, {
|
|
321
|
-
apiToken: this.cloudflareToken,
|
|
322
|
-
accountId: this.cloudflareAccountId
|
|
323
|
-
});
|
|
324
|
-
console.log(` ✅ Database created: ${databaseName}`);
|
|
325
|
-
console.log(` 📊 Database ID: ${databaseId}`);
|
|
326
|
-
created = true;
|
|
327
|
-
}
|
|
328
|
-
} catch (apiError) {
|
|
329
|
-
// Check if this is an authentication or permission error
|
|
330
|
-
if (apiError.message.includes('permission denied') || apiError.message.includes('403') || apiError.message.includes('authentication failed') || apiError.message.includes('401')) {
|
|
331
|
-
if (apiError.message.includes('401')) {
|
|
332
|
-
console.log(` ❌ API token authentication failed (invalid/expired token)`);
|
|
333
|
-
console.log(` 🔗 Check/create token at: https://dash.cloudflare.com/profile/api-tokens`);
|
|
334
|
-
} else {
|
|
335
|
-
console.log(` ⚠️ API token lacks D1 database permissions`);
|
|
336
|
-
console.log(` 💡 Required permission: 'Cloudflare D1:Edit'`);
|
|
337
|
-
console.log(` 🔗 Update token at: https://dash.cloudflare.com/profile/api-tokens`);
|
|
338
|
-
}
|
|
339
|
-
console.log(` 🔄 Falling back to OAuth authentication...`);
|
|
340
|
-
console.log(` ⚠️ WARNING: OAuth uses your personal account, not the API token account!`);
|
|
341
|
-
|
|
342
|
-
// Fall back to OAuth-based operations with warning
|
|
343
|
-
console.log(` 🔐 Using OAuth authentication (wrangler CLI)`);
|
|
344
|
-
exists = await databaseExists(databaseName);
|
|
345
|
-
if (exists) {
|
|
346
|
-
console.log(` ✅ Database already exists: ${databaseName}`);
|
|
347
|
-
databaseId = await getDatabaseId(databaseName);
|
|
348
|
-
console.log(` 📊 Existing Database ID: ${databaseId}`);
|
|
349
|
-
} else {
|
|
350
|
-
console.log(` 📦 Creating database: ${databaseName}`);
|
|
351
|
-
databaseId = await createDatabase(databaseName);
|
|
352
|
-
console.log(` ✅ Database created: ${databaseName}`);
|
|
353
|
-
console.log(` 📊 Database ID: ${databaseId}`);
|
|
354
|
-
created = true;
|
|
355
|
-
}
|
|
356
|
-
} else {
|
|
357
|
-
// Re-throw non-auth/permission errors
|
|
358
|
-
throw apiError;
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
} else {
|
|
362
|
-
// Fallback to CLI-based operations (OAuth)
|
|
363
|
-
console.log(` 🔐 Using OAuth authentication (wrangler CLI)`);
|
|
364
|
-
exists = await databaseExists(databaseName);
|
|
365
|
-
if (exists) {
|
|
366
|
-
console.log(` ✅ Database already exists: ${databaseName}`);
|
|
367
|
-
databaseId = await getDatabaseId(databaseName);
|
|
368
|
-
console.log(` 📊 Existing Database ID: ${databaseId}`);
|
|
369
|
-
} else {
|
|
370
|
-
console.log(` 📦 Creating database: ${databaseName}`);
|
|
371
|
-
databaseId = await createDatabase(databaseName);
|
|
372
|
-
console.log(` ✅ Database created: ${databaseName}`);
|
|
373
|
-
console.log(` 📊 Database ID: ${databaseId}`);
|
|
374
|
-
created = true;
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// Store database info in domain state
|
|
379
|
-
const domainState = this.portfolioState.domainStates.get(domain);
|
|
380
|
-
if (domainState) {
|
|
381
|
-
domainState.databaseName = databaseName;
|
|
382
|
-
domainState.databaseId = databaseId;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
// CRITICAL: Update wrangler.toml BEFORE attempting migrations
|
|
386
|
-
console.log(` 📝 Configuring wrangler.toml for database...`);
|
|
387
|
-
console.log(` 📁 Service path: ${this.servicePath}`);
|
|
388
|
-
console.log(` 📁 Current working directory: ${process.cwd()}`);
|
|
389
|
-
try {
|
|
390
|
-
// Set account_id if API credentials are available
|
|
391
|
-
if (this.cloudflareAccountId) {
|
|
392
|
-
await this.wranglerConfigManager.setAccountId(this.cloudflareAccountId);
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
// Ensure environment section exists
|
|
396
|
-
await this.wranglerConfigManager.ensureEnvironment(this.environment);
|
|
397
|
-
|
|
398
|
-
// Add database binding (use snake_case for wrangler.toml compatibility)
|
|
399
|
-
await this.wranglerConfigManager.addDatabaseBinding(this.environment, {
|
|
400
|
-
binding: 'DB',
|
|
401
|
-
database_name: databaseName,
|
|
402
|
-
database_id: databaseId
|
|
403
|
-
});
|
|
404
|
-
console.log(` ✅ wrangler.toml updated with database configuration`);
|
|
405
|
-
console.log(` 📄 wrangler.toml location: ${this.wranglerConfigManager.configPath}`);
|
|
406
|
-
} catch (configError) {
|
|
407
|
-
console.warn(` ⚠️ Failed to update wrangler.toml: ${configError.message}`);
|
|
408
|
-
console.warn(` 💡 You may need to manually add database configuration`);
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// Apply migrations using DatabaseOrchestrator's enterprise capabilities
|
|
412
|
-
console.log(` 🔄 Applying database migrations...`);
|
|
413
|
-
try {
|
|
414
|
-
// Use the real applyDatabaseMigrations method
|
|
415
|
-
// Note: bindingName defaults to 'DB' if not provided
|
|
416
|
-
// Since databases are created remotely via Cloudflare API, always use remote flag
|
|
417
|
-
await this.databaseOrchestrator.applyDatabaseMigrations(databaseName, 'DB',
|
|
418
|
-
// bindingName - wrangler.toml binding name
|
|
419
|
-
this.environment, true // Always remote since databases are created in Cloudflare
|
|
420
|
-
);
|
|
421
|
-
console.log(` ✅ Migrations applied successfully`);
|
|
422
|
-
} catch (migrationError) {
|
|
423
|
-
console.warn(` ⚠️ Migration warning: ${migrationError.message}`);
|
|
424
|
-
console.warn(` 💡 Migrations can be applied manually later`);
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
// Log comprehensive audit event
|
|
428
|
-
this.stateManager.logAuditEvent(created ? 'DATABASE_CREATED' : 'DATABASE_FOUND', domain, {
|
|
429
|
-
databaseName,
|
|
430
|
-
databaseId,
|
|
431
|
-
environment: this.environment,
|
|
432
|
-
migrationsApplied: true,
|
|
433
|
-
isRemote: true,
|
|
434
|
-
// Always remote since databases are created in Cloudflare
|
|
435
|
-
created
|
|
436
|
-
});
|
|
437
|
-
return {
|
|
438
|
-
databaseName,
|
|
439
|
-
databaseId,
|
|
440
|
-
created,
|
|
441
|
-
migrationsApplied: true
|
|
442
|
-
};
|
|
443
|
-
} catch (error) {
|
|
444
|
-
console.error(` ❌ Database creation failed: ${error.message}`);
|
|
445
|
-
throw error;
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
/**
|
|
450
|
-
* Handle domain secrets using EnhancedSecretManager
|
|
451
|
-
*/
|
|
452
|
-
async handleDomainSecrets(domain) {
|
|
453
|
-
console.log(` 🔐 Handling secrets for ${domain}`);
|
|
454
|
-
if (this.dryRun) {
|
|
455
|
-
console.log(` � DRY RUN: Would upload secrets for ${domain}`);
|
|
456
|
-
return {
|
|
457
|
-
secrets: [],
|
|
458
|
-
uploaded: 0
|
|
459
|
-
};
|
|
460
|
-
}
|
|
461
|
-
try {
|
|
462
|
-
// Generate secrets for this domain using EnhancedSecretManager
|
|
463
|
-
// Use the actual method: generateDomainSpecificSecrets
|
|
464
|
-
const secretResult = await this.secretManager.generateDomainSpecificSecrets(domain, this.environment, {
|
|
465
|
-
customConfigs: {},
|
|
466
|
-
reuseExisting: true,
|
|
467
|
-
rotateAll: false,
|
|
468
|
-
formats: ['env', 'wrangler'] // Generate both .env and wrangler CLI formats
|
|
469
|
-
});
|
|
470
|
-
const secrets = secretResult.secrets || {};
|
|
471
|
-
const secretNames = Object.keys(secrets);
|
|
472
|
-
if (secretNames.length > 0) {
|
|
473
|
-
console.log(` ✅ Generated ${secretNames.length} secrets: ${secretNames.join(', ')}`);
|
|
474
|
-
console.log(` 🔒 Secret values are encrypted and not displayed`);
|
|
475
|
-
console.log(` 📄 Distribution files: ${secretResult.distributionFiles?.join(', ') || 'N/A'}`);
|
|
476
|
-
|
|
477
|
-
// Log audit event with full metadata
|
|
478
|
-
this.stateManager.logAuditEvent('SECRETS_GENERATED', domain, {
|
|
479
|
-
count: secretNames.length,
|
|
480
|
-
names: secretNames,
|
|
481
|
-
environment: this.environment,
|
|
482
|
-
formats: secretResult.formats || [],
|
|
483
|
-
distributionPath: secretResult.distributionPath
|
|
484
|
-
});
|
|
485
|
-
} else {
|
|
486
|
-
console.log(` ℹ️ No secrets to upload for ${domain}`);
|
|
487
|
-
}
|
|
488
|
-
return {
|
|
489
|
-
secrets: secretNames,
|
|
490
|
-
uploaded: secretNames.length,
|
|
491
|
-
distributionPath: secretResult.distributionPath,
|
|
492
|
-
formats: secretResult.formats
|
|
493
|
-
};
|
|
494
|
-
} catch (error) {
|
|
495
|
-
console.error(` ⚠️ Secret generation failed: ${error.message}`);
|
|
496
|
-
// Don't fail deployment if secrets fail - they can be added manually
|
|
497
|
-
return {
|
|
498
|
-
secrets: [],
|
|
499
|
-
uploaded: 0,
|
|
500
|
-
error: error.message
|
|
501
|
-
};
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
/**
|
|
506
|
-
* Deploy domain worker (executes actual wrangler deploy)
|
|
507
|
-
*/
|
|
508
|
-
async deployDomainWorker(domain) {
|
|
509
|
-
console.log(` 🚀 Deploying worker for ${domain}`);
|
|
510
|
-
if (this.dryRun) {
|
|
511
|
-
console.log(` 🔍 DRY RUN: Would deploy worker for ${domain}`);
|
|
512
|
-
const subdomain = this.environment === 'production' ? 'api' : `${this.environment}-api`;
|
|
513
|
-
return {
|
|
514
|
-
url: `https://${subdomain}.${domain}`,
|
|
515
|
-
deployed: false,
|
|
516
|
-
dryRun: true
|
|
517
|
-
};
|
|
518
|
-
}
|
|
519
|
-
try {
|
|
520
|
-
// CRITICAL: Ensure environment section exists in wrangler.toml BEFORE deploying
|
|
521
|
-
console.log(` 📝 Verifying wrangler.toml configuration...`);
|
|
522
|
-
try {
|
|
523
|
-
await this.wranglerConfigManager.ensureEnvironment(this.environment);
|
|
524
|
-
} catch (configError) {
|
|
525
|
-
console.warn(` ⚠️ Could not verify wrangler.toml: ${configError.message}`);
|
|
526
|
-
// Continue anyway - wrangler will provide clearer error if config is wrong
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
// Find wrangler.toml in service path
|
|
530
|
-
const wranglerConfigPath = join(this.servicePath, 'wrangler.toml');
|
|
531
|
-
|
|
532
|
-
// Build deploy command with environment
|
|
533
|
-
let deployCommand = `npx wrangler deploy`;
|
|
534
|
-
|
|
535
|
-
// Add environment flag for non-production
|
|
536
|
-
if (this.environment !== 'production') {
|
|
537
|
-
deployCommand += ` --env ${this.environment}`;
|
|
538
|
-
}
|
|
539
|
-
console.log(` � Executing: ${deployCommand}`);
|
|
540
|
-
console.log(` 📁 Working directory: ${this.servicePath}`);
|
|
541
|
-
|
|
542
|
-
// Execute deployment with timeout
|
|
543
|
-
const {
|
|
544
|
-
stdout,
|
|
545
|
-
stderr
|
|
546
|
-
} = await execAsync(deployCommand, {
|
|
547
|
-
cwd: this.servicePath,
|
|
548
|
-
timeout: 120000,
|
|
549
|
-
// 2 minute timeout
|
|
550
|
-
maxBuffer: 1024 * 1024 * 10 // 10MB buffer for large outputs
|
|
551
|
-
});
|
|
552
|
-
|
|
553
|
-
// Log output for debugging
|
|
554
|
-
if (stdout) {
|
|
555
|
-
console.log(` 📄 Deployment output:`);
|
|
556
|
-
stdout.split('\n').filter(line => line.trim()).forEach(line => {
|
|
557
|
-
console.log(` ${line}`);
|
|
558
|
-
});
|
|
559
|
-
}
|
|
560
|
-
if (stderr && !stderr.includes('deprecated')) {
|
|
561
|
-
console.warn(` ⚠️ Deployment warnings: ${stderr}`);
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
// Parse worker URL from wrangler output
|
|
565
|
-
// Wrangler outputs: "Published service-name (version) to https://worker-url"
|
|
566
|
-
const urlMatch = stdout.match(/https:\/\/[^\s]+/);
|
|
567
|
-
const workerUrl = urlMatch ? urlMatch[0] : null;
|
|
568
|
-
|
|
569
|
-
// Also construct custom domain URL
|
|
570
|
-
const subdomain = this.environment === 'production' ? 'api' : `${this.environment}-api`;
|
|
571
|
-
const customUrl = `https://${subdomain}.${domain}`;
|
|
572
|
-
|
|
573
|
-
// Store URLs in domain state
|
|
574
|
-
const domainState = this.portfolioState.domainStates.get(domain);
|
|
575
|
-
if (domainState) {
|
|
576
|
-
domainState.workerUrl = workerUrl;
|
|
577
|
-
domainState.deploymentUrl = customUrl;
|
|
578
|
-
}
|
|
579
|
-
if (workerUrl) {
|
|
580
|
-
console.log(` ✅ Worker deployed successfully`);
|
|
581
|
-
console.log(` 🔗 Worker URL: ${workerUrl}`);
|
|
582
|
-
console.log(` 🔗 Custom URL: ${customUrl}`);
|
|
583
|
-
} else {
|
|
584
|
-
console.log(` ✅ Deployment completed (URL not detected in output)`);
|
|
585
|
-
console.log(` 🔗 Expected URL: ${customUrl}`);
|
|
586
|
-
}
|
|
587
|
-
return {
|
|
588
|
-
url: customUrl,
|
|
589
|
-
workerUrl: workerUrl,
|
|
590
|
-
deployed: true,
|
|
591
|
-
stdout,
|
|
592
|
-
stderr
|
|
593
|
-
};
|
|
594
|
-
} catch (error) {
|
|
595
|
-
console.error(` ❌ Worker deployment failed: ${error.message}`);
|
|
596
|
-
|
|
597
|
-
// Parse error for helpful diagnostics
|
|
598
|
-
if (error.message.includes('wrangler.toml')) {
|
|
599
|
-
console.error(` 💡 Ensure wrangler.toml exists in ${this.servicePath}`);
|
|
600
|
-
}
|
|
601
|
-
if (error.message.includes('No environment found')) {
|
|
602
|
-
console.error(` 💡 Add [env.${this.environment}] section to wrangler.toml`);
|
|
603
|
-
}
|
|
604
|
-
if (error.stderr) {
|
|
605
|
-
console.error(` 📄 Error details: ${error.stderr}`);
|
|
606
|
-
}
|
|
607
|
-
throw new Error(`Worker deployment failed for ${domain}: ${error.message}`);
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
/**
|
|
612
|
-
* Validate domain deployment with real HTTP health check (with retries)
|
|
613
|
-
*/
|
|
614
|
-
async validateDomainDeployment(domain) {
|
|
615
|
-
console.log(` ✅ Validating deployment for ${domain}`);
|
|
616
|
-
if (this.dryRun || this.skipTests) {
|
|
617
|
-
console.log(` ⏭️ Skipping health check (${this.dryRun ? 'dry run' : 'tests disabled'})`);
|
|
618
|
-
return true;
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
// Get the deployment URL from domain state
|
|
622
|
-
const domainState = this.portfolioState.domainStates.get(domain);
|
|
623
|
-
const deploymentUrl = domainState?.deploymentUrl;
|
|
624
|
-
if (!deploymentUrl) {
|
|
625
|
-
console.log(` ⚠️ No deployment URL found, skipping health check`);
|
|
626
|
-
return true;
|
|
627
|
-
}
|
|
628
|
-
console.log(` 🔍 Running health check: ${deploymentUrl}/health`);
|
|
629
|
-
|
|
630
|
-
// Retry logic for health checks
|
|
631
|
-
const maxRetries = 3;
|
|
632
|
-
const retryDelay = 5000; // 5 seconds between retries
|
|
633
|
-
|
|
634
|
-
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
635
|
-
try {
|
|
636
|
-
const startTime = Date.now();
|
|
637
|
-
console.log(` Attempt ${attempt}/${maxRetries}...`);
|
|
638
|
-
|
|
639
|
-
// Perform actual HTTP health check
|
|
640
|
-
const response = await fetch(`${deploymentUrl}/health`, {
|
|
641
|
-
method: 'GET',
|
|
642
|
-
headers: {
|
|
643
|
-
'User-Agent': 'Clodo-Orchestrator/2.0'
|
|
644
|
-
},
|
|
645
|
-
signal: AbortSignal.timeout(15000) // 15 second timeout
|
|
646
|
-
});
|
|
647
|
-
const responseTime = Date.now() - startTime;
|
|
648
|
-
const status = response.status;
|
|
649
|
-
if (status === 200) {
|
|
650
|
-
console.log(` ✅ Health check passed (${status}) - Response time: ${responseTime}ms`);
|
|
651
|
-
|
|
652
|
-
// Log successful health check
|
|
653
|
-
this.stateManager.logAuditEvent('HEALTH_CHECK_PASSED', domain, {
|
|
654
|
-
url: deploymentUrl,
|
|
655
|
-
status,
|
|
656
|
-
responseTime,
|
|
657
|
-
attempt,
|
|
658
|
-
environment: this.environment
|
|
659
|
-
});
|
|
660
|
-
return true;
|
|
661
|
-
} else {
|
|
662
|
-
const errorMsg = `Health check returned ${status} - deployment may have issues`;
|
|
663
|
-
console.log(` ⚠️ ${errorMsg}`);
|
|
664
|
-
this.stateManager.logAuditEvent('HEALTH_CHECK_WARNING', domain, {
|
|
665
|
-
url: deploymentUrl,
|
|
666
|
-
status,
|
|
667
|
-
responseTime,
|
|
668
|
-
attempt,
|
|
669
|
-
environment: this.environment
|
|
670
|
-
});
|
|
671
|
-
|
|
672
|
-
// Don't fail deployment for non-200 status, just warn
|
|
673
|
-
return true;
|
|
674
|
-
}
|
|
675
|
-
} catch (error) {
|
|
676
|
-
const isLastAttempt = attempt === maxRetries;
|
|
677
|
-
const errorMsg = `Health check failed: ${error.message}`;
|
|
678
|
-
if (isLastAttempt) {
|
|
679
|
-
console.log(` ❌ ${errorMsg} (final attempt)`);
|
|
680
|
-
console.log(` 💡 The service may still be deploying. Check manually: curl ${deploymentUrl}/health`);
|
|
681
|
-
this.stateManager.logAuditEvent('HEALTH_CHECK_FAILED', domain, {
|
|
682
|
-
url: deploymentUrl,
|
|
683
|
-
error: error.message,
|
|
684
|
-
attempts: maxRetries,
|
|
685
|
-
environment: this.environment
|
|
686
|
-
});
|
|
687
|
-
|
|
688
|
-
// Don't fail deployment for health check failure - it might just need time
|
|
689
|
-
return true;
|
|
690
|
-
} else {
|
|
691
|
-
console.log(` ⚠️ ${errorMsg} (attempt ${attempt}/${maxRetries})`);
|
|
692
|
-
console.log(` ⏳ Retrying in ${retryDelay / 1000} seconds...`);
|
|
693
|
-
await new Promise(resolve => setTimeout(resolve, retryDelay));
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
return true;
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
/**
|
|
701
|
-
* Get rollback plan using state manager
|
|
702
|
-
* @returns {Array} Rollback plan from state manager
|
|
703
|
-
*/
|
|
704
|
-
getRollbackPlan() {
|
|
705
|
-
return this.stateManager.portfolioState.rollbackPlan;
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
/**
|
|
709
|
-
* Execute rollback using state manager
|
|
710
|
-
* @returns {Promise<Object>} Rollback result
|
|
711
|
-
*/
|
|
712
|
-
async executeRollback() {
|
|
713
|
-
return await this.stateManager.executeRollback();
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
/**
|
|
717
|
-
* Get portfolio statistics from state manager
|
|
718
|
-
* @returns {Object} Portfolio statistics
|
|
719
|
-
*/
|
|
720
|
-
getPortfolioStats() {
|
|
721
|
-
return this.stateManager.getPortfolioSummary();
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
export default MultiDomainOrchestrator;
|