@tamyla/clodo-framework 1.0.0
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 +564 -0
- package/LICENSE +21 -0
- package/README.md +1393 -0
- package/bin/README.md +71 -0
- package/bin/clodo-service.js +416 -0
- package/bin/security/security-cli.js +96 -0
- package/bin/service-management/README.md +74 -0
- package/bin/service-management/create-service.js +129 -0
- package/bin/service-management/init-service.js +102 -0
- package/bin/service-management/init-service.js.backup +889 -0
- package/bin/shared/config/customer-cli.js +293 -0
- package/dist/config/ConfigurationManager.js +159 -0
- package/dist/config/CustomerConfigCLI.js +220 -0
- package/dist/config/FeatureManager.js +426 -0
- package/dist/config/customers.js +441 -0
- package/dist/config/domains.js +180 -0
- package/dist/config/features.js +225 -0
- package/dist/config/index.js +6 -0
- package/dist/database/database-orchestrator.js +730 -0
- package/dist/database/index.js +4 -0
- package/dist/deployment/auditor.js +971 -0
- package/dist/deployment/index.js +10 -0
- package/dist/deployment/rollback-manager.js +523 -0
- package/dist/deployment/testers/api-tester.js +80 -0
- package/dist/deployment/testers/auth-tester.js +129 -0
- package/dist/deployment/testers/core.js +217 -0
- package/dist/deployment/testers/database-tester.js +105 -0
- package/dist/deployment/testers/index.js +74 -0
- package/dist/deployment/testers/load-tester.js +120 -0
- package/dist/deployment/testers/performance-tester.js +105 -0
- package/dist/deployment/validator.js +558 -0
- package/dist/deployment/wrangler-deployer.js +574 -0
- package/dist/handlers/GenericRouteHandler.js +532 -0
- package/dist/index.js +39 -0
- package/dist/migration/MigrationAdapters.js +562 -0
- package/dist/modules/ModuleManager.js +668 -0
- package/dist/modules/security.js +98 -0
- package/dist/orchestration/cross-domain-coordinator.js +1083 -0
- package/dist/orchestration/index.js +5 -0
- package/dist/orchestration/modules/DeploymentCoordinator.js +258 -0
- package/dist/orchestration/modules/DomainResolver.js +196 -0
- package/dist/orchestration/modules/StateManager.js +332 -0
- package/dist/orchestration/multi-domain-orchestrator.js +255 -0
- package/dist/routing/EnhancedRouter.js +158 -0
- package/dist/schema/SchemaManager.js +778 -0
- package/dist/security/ConfigurationValidator.js +490 -0
- package/dist/security/DeploymentManager.js +208 -0
- package/dist/security/SecretGenerator.js +142 -0
- package/dist/security/SecurityCLI.js +228 -0
- package/dist/security/index.js +51 -0
- package/dist/security/patterns/environment-rules.js +66 -0
- package/dist/security/patterns/insecure-patterns.js +21 -0
- package/dist/service-management/ConfirmationEngine.js +411 -0
- package/dist/service-management/ErrorTracker.js +294 -0
- package/dist/service-management/GenerationEngine.js +3109 -0
- package/dist/service-management/InputCollector.js +237 -0
- package/dist/service-management/ServiceCreator.js +229 -0
- package/dist/service-management/ServiceInitializer.js +448 -0
- package/dist/service-management/ServiceOrchestrator.js +638 -0
- package/dist/service-management/handlers/ConfigMutator.js +130 -0
- package/dist/service-management/handlers/ConfirmationHandler.js +71 -0
- package/dist/service-management/handlers/GenerationHandler.js +80 -0
- package/dist/service-management/handlers/InputHandler.js +59 -0
- package/dist/service-management/handlers/ValidationHandler.js +203 -0
- package/dist/service-management/index.js +7 -0
- package/dist/services/GenericDataService.js +488 -0
- package/dist/shared/cloudflare/domain-discovery.js +562 -0
- package/dist/shared/cloudflare/domain-manager.js +912 -0
- package/dist/shared/cloudflare/index.js +8 -0
- package/dist/shared/cloudflare/ops.js +387 -0
- package/dist/shared/config/cache.js +1167 -0
- package/dist/shared/config/command-config-manager.js +174 -0
- package/dist/shared/config/customer-cli.js +258 -0
- package/dist/shared/config/index.js +9 -0
- package/dist/shared/config/manager.js +289 -0
- package/dist/shared/database/connection-manager.js +338 -0
- package/dist/shared/database/index.js +7 -0
- package/dist/shared/database/orchestrator.js +632 -0
- package/dist/shared/deployment/auditor.js +971 -0
- package/dist/shared/deployment/index.js +10 -0
- package/dist/shared/deployment/rollback-manager.js +523 -0
- package/dist/shared/deployment/validator.js +558 -0
- package/dist/shared/index.js +32 -0
- package/dist/shared/monitoring/health-checker.js +250 -0
- package/dist/shared/monitoring/index.js +8 -0
- package/dist/shared/monitoring/memory-manager.js +382 -0
- package/dist/shared/monitoring/production-monitor.js +390 -0
- package/dist/shared/production-tester/api-tester.js +80 -0
- package/dist/shared/production-tester/auth-tester.js +129 -0
- package/dist/shared/production-tester/core.js +217 -0
- package/dist/shared/production-tester/database-tester.js +105 -0
- package/dist/shared/production-tester/index.js +74 -0
- package/dist/shared/production-tester/load-tester.js +120 -0
- package/dist/shared/production-tester/performance-tester.js +105 -0
- package/dist/shared/security/api-token-manager.js +296 -0
- package/dist/shared/security/index.js +8 -0
- package/dist/shared/security/secret-generator.js +918 -0
- package/dist/shared/security/secure-token-manager.js +379 -0
- package/dist/shared/utils/error-recovery.js +240 -0
- package/dist/shared/utils/graceful-shutdown-manager.js +380 -0
- package/dist/shared/utils/index.js +9 -0
- package/dist/shared/utils/interactive-prompts.js +134 -0
- package/dist/shared/utils/rate-limiter.js +249 -0
- package/dist/utils/ErrorHandler.js +173 -0
- package/dist/utils/deployment/config-cache.js +1160 -0
- package/dist/utils/deployment/index.js +6 -0
- package/dist/utils/deployment/interactive-prompts.js +97 -0
- package/dist/utils/deployment/secret-generator.js +896 -0
- package/dist/utils/dirname-helper.js +35 -0
- package/dist/utils/domain-config.js +159 -0
- package/dist/utils/error-recovery.js +240 -0
- package/dist/utils/esm-helper.js +52 -0
- package/dist/utils/framework-config.js +481 -0
- package/dist/utils/graceful-shutdown-manager.js +379 -0
- package/dist/utils/health-checker.js +114 -0
- package/dist/utils/index.js +36 -0
- package/dist/utils/prompt-handler.js +98 -0
- package/dist/utils/usage-tracker.js +252 -0
- package/dist/utils/validation.js +112 -0
- package/dist/version/VersionDetector.js +723 -0
- package/dist/worker/index.js +4 -0
- package/dist/worker/integration.js +332 -0
- package/docs/FRAMEWORK-ARCHITECTURE-OVERVIEW.md +206 -0
- package/docs/INTEGRATION_GUIDE.md +2045 -0
- package/docs/README.md +82 -0
- package/docs/SECURITY.md +242 -0
- package/docs/deployment/deployment-guide.md +540 -0
- package/docs/overview.md +280 -0
- package/package.json +176 -0
- package/types/index.d.ts +575 -0
|
@@ -0,0 +1,1083 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enterprise Cross-Domain Coordinator
|
|
3
|
+
*
|
|
4
|
+
* Advanced portfolio management system for synchronized multi-domain operations with:
|
|
5
|
+
* - Domain portfolio discovery and mapping
|
|
6
|
+
* - Cross-domain deployment coordination and synchronization
|
|
7
|
+
* - Shared resource management across domains
|
|
8
|
+
* - Dependency resolution and deployment ordering
|
|
9
|
+
* - Cross-domain compatibility validation
|
|
10
|
+
* - Portfolio-wide monitoring and health checks
|
|
11
|
+
* - Shared configuration and secret coordination
|
|
12
|
+
* - Multi-domain rollback and recovery
|
|
13
|
+
* - Cross-environment consistency verification
|
|
14
|
+
* - Enterprise-grade audit and compliance tracking
|
|
15
|
+
*
|
|
16
|
+
* @module cross-domain-coordinator
|
|
17
|
+
* @version 2.0.0
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { access } from 'fs/promises';
|
|
21
|
+
import { MultiDomainOrchestrator } from './multi-domain-orchestrator.js';
|
|
22
|
+
import { DeploymentValidator } from '../../bin/shared/deployment/validator.js';
|
|
23
|
+
import { RollbackManager } from '../../bin/shared/deployment/rollback-manager.js';
|
|
24
|
+
import { DomainDiscovery } from '../../bin/shared/cloudflare/domain-discovery.js';
|
|
25
|
+
import { DatabaseOrchestrator } from '../database/database-orchestrator.js';
|
|
26
|
+
import { EnhancedSecretManager } from '../utils/deployment/secret-generator.js';
|
|
27
|
+
import { ProductionTester } from '../../bin/shared/production-tester/index.js';
|
|
28
|
+
import { DeploymentAuditor } from '../../bin/shared/deployment/auditor.js';
|
|
29
|
+
import { ConfigurationCacheManager } from '../utils/deployment/config-cache.js';
|
|
30
|
+
export class CrossDomainCoordinator {
|
|
31
|
+
constructor(options = {}) {
|
|
32
|
+
this.config = {
|
|
33
|
+
// Portfolio configuration
|
|
34
|
+
portfolioName: options.portfolioName || 'enterprise-portfolio',
|
|
35
|
+
maxConcurrentDeployments: options.maxConcurrentDeployments || 3,
|
|
36
|
+
deploymentTimeout: options.deploymentTimeout || 1800000,
|
|
37
|
+
// 30 minutes
|
|
38
|
+
|
|
39
|
+
// Coordination settings
|
|
40
|
+
enableDependencyResolution: options.enableDependencyResolution !== false,
|
|
41
|
+
enableCrossValidation: options.enableCrossValidation !== false,
|
|
42
|
+
enableSharedResources: options.enableSharedResources !== false,
|
|
43
|
+
// Monitoring and health
|
|
44
|
+
healthCheckInterval: options.healthCheckInterval || 300000,
|
|
45
|
+
// 5 minutes
|
|
46
|
+
enableContinuousMonitoring: options.enableContinuousMonitoring !== false,
|
|
47
|
+
// Rollback and recovery
|
|
48
|
+
enableAutoRollback: options.enableAutoRollback !== false,
|
|
49
|
+
rollbackThreshold: options.rollbackThreshold || 0.8,
|
|
50
|
+
// 80% success rate
|
|
51
|
+
|
|
52
|
+
// Environments
|
|
53
|
+
environments: options.environments || ['development', 'staging', 'production'],
|
|
54
|
+
defaultEnvironment: options.defaultEnvironment || 'production',
|
|
55
|
+
// Integration modules
|
|
56
|
+
useAllModules: options.useAllModules !== false,
|
|
57
|
+
moduleOptions: options.moduleOptions || {}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Initialize portfolio state
|
|
61
|
+
this.portfolio = {
|
|
62
|
+
domains: new Map(),
|
|
63
|
+
deployments: new Map(),
|
|
64
|
+
sharedResources: new Map(),
|
|
65
|
+
dependencies: new Map(),
|
|
66
|
+
healthStatus: new Map(),
|
|
67
|
+
metrics: {
|
|
68
|
+
totalDomains: 0,
|
|
69
|
+
activeDeployments: 0,
|
|
70
|
+
completedDeployments: 0,
|
|
71
|
+
failedDeployments: 0,
|
|
72
|
+
rolledBackDeployments: 0
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Coordination session
|
|
77
|
+
this.session = {
|
|
78
|
+
sessionId: this.generateSessionId(),
|
|
79
|
+
startTime: new Date(),
|
|
80
|
+
operations: [],
|
|
81
|
+
coordinationState: 'idle'
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Initialize the cross-domain coordinator
|
|
87
|
+
* @returns {Promise<void>}
|
|
88
|
+
*/
|
|
89
|
+
async initialize() {
|
|
90
|
+
// Initialize enterprise modules
|
|
91
|
+
this.modules = this.initializeEnterpriseModules();
|
|
92
|
+
|
|
93
|
+
// Initialize async modules
|
|
94
|
+
await this.modules.configCache.initialize();
|
|
95
|
+
console.log('🎯 Cross-Domain Coordinator initialized');
|
|
96
|
+
console.log(` 📊 Portfolio: ${this.config.portfolioName}`);
|
|
97
|
+
console.log(` 🔄 Max Concurrent: ${this.config.maxConcurrentDeployments}`);
|
|
98
|
+
console.log(` 📋 Session: ${this.session.sessionId}`);
|
|
99
|
+
this.auditCoordinatorEvent('COORDINATOR_INITIALIZED', {
|
|
100
|
+
portfolioName: this.config.portfolioName,
|
|
101
|
+
sessionId: this.session.sessionId,
|
|
102
|
+
config: this.config
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Initialize all enterprise modules with coordination support
|
|
108
|
+
* @returns {Object} Initialized modules
|
|
109
|
+
*/
|
|
110
|
+
initializeEnterpriseModules() {
|
|
111
|
+
const moduleOptions = this.config.moduleOptions;
|
|
112
|
+
return {
|
|
113
|
+
orchestrator: new MultiDomainOrchestrator({
|
|
114
|
+
maxConcurrentDeployments: this.config.maxConcurrentDeployments,
|
|
115
|
+
coordinationMode: true,
|
|
116
|
+
...moduleOptions.orchestrator
|
|
117
|
+
}),
|
|
118
|
+
validator: new DeploymentValidator({
|
|
119
|
+
crossDomainValidation: this.config.enableCrossValidation,
|
|
120
|
+
portfolioMode: true,
|
|
121
|
+
...moduleOptions.validator
|
|
122
|
+
}),
|
|
123
|
+
rollbackManager: new RollbackManager({
|
|
124
|
+
autoRollbackEnabled: this.config.enableAutoRollback,
|
|
125
|
+
crossDomainRollback: true,
|
|
126
|
+
...moduleOptions.rollbackManager
|
|
127
|
+
}),
|
|
128
|
+
domainDiscovery: new DomainDiscovery({
|
|
129
|
+
portfolioDiscovery: true,
|
|
130
|
+
batchDiscovery: true,
|
|
131
|
+
...moduleOptions.domainDiscovery
|
|
132
|
+
}),
|
|
133
|
+
databaseOrchestrator: new DatabaseOrchestrator({
|
|
134
|
+
crossDomainOperations: true,
|
|
135
|
+
portfolioMode: true,
|
|
136
|
+
...moduleOptions.databaseOrchestrator
|
|
137
|
+
}),
|
|
138
|
+
secretManager: new EnhancedSecretManager({
|
|
139
|
+
crossDomainCoordination: true,
|
|
140
|
+
portfolioSecrets: true,
|
|
141
|
+
...moduleOptions.secretManager
|
|
142
|
+
}),
|
|
143
|
+
productionTester: new ProductionTester({
|
|
144
|
+
portfolioTesting: true,
|
|
145
|
+
crossDomainTests: true,
|
|
146
|
+
...moduleOptions.productionTester
|
|
147
|
+
}),
|
|
148
|
+
auditor: new DeploymentAuditor({
|
|
149
|
+
portfolioAudit: true,
|
|
150
|
+
crossDomainTracking: true,
|
|
151
|
+
...moduleOptions.auditor
|
|
152
|
+
}),
|
|
153
|
+
configCache: new ConfigurationCacheManager({
|
|
154
|
+
portfolioConfigs: true,
|
|
155
|
+
crossDomainSharing: true,
|
|
156
|
+
...moduleOptions.configCache
|
|
157
|
+
})
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Discover and register entire domain portfolio
|
|
163
|
+
* @param {Array|Object} domainSources - Domain sources (configs, discovery, etc.)
|
|
164
|
+
* @returns {Promise<Object>} Portfolio discovery results
|
|
165
|
+
*/
|
|
166
|
+
async discoverPortfolio(domainSources = []) {
|
|
167
|
+
console.log('🔍 Discovering domain portfolio...');
|
|
168
|
+
const discoverySession = {
|
|
169
|
+
sessionId: this.generateOperationId(),
|
|
170
|
+
startTime: new Date(),
|
|
171
|
+
domains: [],
|
|
172
|
+
errors: []
|
|
173
|
+
};
|
|
174
|
+
try {
|
|
175
|
+
// Method 1: From provided domain list
|
|
176
|
+
if (Array.isArray(domainSources) && domainSources.length > 0) {
|
|
177
|
+
await this.discoverFromDomainList(domainSources, discoverySession);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Method 2: From configuration files
|
|
181
|
+
await this.discoverFromConfigurations(discoverySession);
|
|
182
|
+
|
|
183
|
+
// Method 3: From runtime discovery (Cloudflare API)
|
|
184
|
+
if (this.config.enableCrossValidation) {
|
|
185
|
+
await this.discoverFromRuntime(discoverySession);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Process discovered domains
|
|
189
|
+
for (const domainInfo of discoverySession.domains) {
|
|
190
|
+
await this.registerDomain(domainInfo);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Build dependency graph
|
|
194
|
+
if (this.config.enableDependencyResolution) {
|
|
195
|
+
await this.buildDependencyGraph();
|
|
196
|
+
}
|
|
197
|
+
discoverySession.endTime = new Date();
|
|
198
|
+
discoverySession.duration = (discoverySession.endTime - discoverySession.startTime) / 1000;
|
|
199
|
+
this.auditCoordinatorEvent('PORTFOLIO_DISCOVERY_COMPLETED', {
|
|
200
|
+
sessionId: discoverySession.sessionId,
|
|
201
|
+
domainsFound: discoverySession.domains.length,
|
|
202
|
+
duration: discoverySession.duration,
|
|
203
|
+
errors: discoverySession.errors.length
|
|
204
|
+
});
|
|
205
|
+
console.log(`✅ Portfolio discovery completed: ${discoverySession.domains.length} domains found`);
|
|
206
|
+
return {
|
|
207
|
+
domains: discoverySession.domains,
|
|
208
|
+
totalDomains: discoverySession.domains.length,
|
|
209
|
+
errors: discoverySession.errors,
|
|
210
|
+
dependencies: Array.from(this.portfolio.dependencies.entries())
|
|
211
|
+
};
|
|
212
|
+
} catch (error) {
|
|
213
|
+
console.error(`❌ Portfolio discovery failed: ${error.message}`);
|
|
214
|
+
this.auditCoordinatorEvent('PORTFOLIO_DISCOVERY_FAILED', {
|
|
215
|
+
sessionId: discoverySession.sessionId,
|
|
216
|
+
error: error.message
|
|
217
|
+
});
|
|
218
|
+
throw error;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Discover domains from provided list
|
|
224
|
+
* @param {Array} domainList - List of domain names or configurations
|
|
225
|
+
* @param {Object} session - Discovery session
|
|
226
|
+
*/
|
|
227
|
+
async discoverFromDomainList(domainList, session) {
|
|
228
|
+
console.log(` 📋 Processing ${domainList.length} provided domains...`);
|
|
229
|
+
for (const domainInput of domainList) {
|
|
230
|
+
try {
|
|
231
|
+
const domainInfo = typeof domainInput === 'string' ? await this.modules.domainDiscovery.discoverDomainConfig(domainInput) : domainInput;
|
|
232
|
+
session.domains.push({
|
|
233
|
+
...domainInfo,
|
|
234
|
+
source: 'provided',
|
|
235
|
+
discoveredAt: new Date()
|
|
236
|
+
});
|
|
237
|
+
console.log(` ✅ ${domainInfo.name || domainInput}`);
|
|
238
|
+
} catch (error) {
|
|
239
|
+
session.errors.push({
|
|
240
|
+
domain: domainInput,
|
|
241
|
+
error: error.message,
|
|
242
|
+
source: 'provided'
|
|
243
|
+
});
|
|
244
|
+
console.log(` ❌ ${domainInput}: ${error.message}`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Discover domains from configuration files
|
|
251
|
+
* @param {Object} session - Discovery session
|
|
252
|
+
*/
|
|
253
|
+
async discoverFromConfigurations(session) {
|
|
254
|
+
console.log(' 📁 Scanning configuration files...');
|
|
255
|
+
try {
|
|
256
|
+
// Look for domains.js configuration
|
|
257
|
+
const domainsConfigPath = 'src/config/domains.js';
|
|
258
|
+
try {
|
|
259
|
+
await access(domainsConfigPath);
|
|
260
|
+
const domainsConfig = await import(`../../${domainsConfigPath}`);
|
|
261
|
+
if (domainsConfig.DOMAINS) {
|
|
262
|
+
for (const [domainName, domainConfig] of Object.entries(domainsConfig.DOMAINS)) {
|
|
263
|
+
session.domains.push({
|
|
264
|
+
...domainConfig,
|
|
265
|
+
name: domainName,
|
|
266
|
+
source: 'configuration',
|
|
267
|
+
discoveredAt: new Date()
|
|
268
|
+
});
|
|
269
|
+
console.log(` ✅ ${domainName} (from config)`);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
} catch {
|
|
273
|
+
// Configuration file not found, continue
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Look for cached configurations
|
|
277
|
+
const cachedConfigs = await this.modules.configCache.getCacheStatistics();
|
|
278
|
+
console.log(` 💾 Found ${cachedConfigs.entries} cached configurations`);
|
|
279
|
+
} catch (error) {
|
|
280
|
+
session.errors.push({
|
|
281
|
+
source: 'configuration',
|
|
282
|
+
error: error.message
|
|
283
|
+
});
|
|
284
|
+
console.log(` ❌ Configuration scan failed: ${error.message}`);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Discover domains from runtime (Cloudflare API)
|
|
290
|
+
* @param {Object} session - Discovery session
|
|
291
|
+
*/
|
|
292
|
+
async discoverFromRuntime(session) {
|
|
293
|
+
console.log(' ☁️ Performing runtime discovery...');
|
|
294
|
+
try {
|
|
295
|
+
// This would use actual Cloudflare API in real implementation
|
|
296
|
+
console.log(' 🔍 Runtime discovery not yet implemented');
|
|
297
|
+
} catch (error) {
|
|
298
|
+
session.errors.push({
|
|
299
|
+
source: 'runtime',
|
|
300
|
+
error: error.message
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Register domain in portfolio
|
|
307
|
+
* @param {Object} domainInfo - Domain information
|
|
308
|
+
*/
|
|
309
|
+
async registerDomain(domainInfo) {
|
|
310
|
+
const domainId = domainInfo.name || domainInfo.domain;
|
|
311
|
+
const domainEntry = {
|
|
312
|
+
id: domainId,
|
|
313
|
+
...domainInfo,
|
|
314
|
+
registeredAt: new Date(),
|
|
315
|
+
status: 'registered',
|
|
316
|
+
deployments: [],
|
|
317
|
+
healthChecks: [],
|
|
318
|
+
dependencies: []
|
|
319
|
+
};
|
|
320
|
+
this.portfolio.domains.set(domainId, domainEntry);
|
|
321
|
+
this.portfolio.metrics.totalDomains++;
|
|
322
|
+
console.log(` 📝 Registered domain: ${domainId}`);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Build dependency graph for portfolio domains
|
|
327
|
+
*/
|
|
328
|
+
async buildDependencyGraph() {
|
|
329
|
+
console.log('🔗 Building dependency graph...');
|
|
330
|
+
const dependencies = new Map();
|
|
331
|
+
for (const [domainId, domainInfo] of this.portfolio.domains) {
|
|
332
|
+
const domainDeps = [];
|
|
333
|
+
|
|
334
|
+
// Check for shared services
|
|
335
|
+
if (domainInfo.services) {
|
|
336
|
+
for (const [, serviceConfig] of Object.entries(domainInfo.services)) {
|
|
337
|
+
// Look for cross-domain dependencies
|
|
338
|
+
if (serviceConfig.dependsOn) {
|
|
339
|
+
domainDeps.push(...serviceConfig.dependsOn);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Check for shared databases
|
|
345
|
+
if (domainInfo.databases) {
|
|
346
|
+
// Add database dependencies
|
|
347
|
+
for (const [, dbConfig] of Object.entries(domainInfo.databases)) {
|
|
348
|
+
if (dbConfig.sharedWith) {
|
|
349
|
+
domainDeps.push(...dbConfig.sharedWith);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (domainDeps.length > 0) {
|
|
354
|
+
dependencies.set(domainId, domainDeps);
|
|
355
|
+
console.log(` 🔗 ${domainId}: depends on ${domainDeps.join(', ')}`);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
this.portfolio.dependencies = dependencies;
|
|
359
|
+
console.log(`✅ Dependency graph built: ${dependencies.size} domains have dependencies`);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Coordinate deployment across multiple domains
|
|
364
|
+
* @param {Array} domains - Domains to deploy
|
|
365
|
+
* @param {Object} options - Deployment options
|
|
366
|
+
* @returns {Promise<Object>} Deployment results
|
|
367
|
+
*/
|
|
368
|
+
async coordinateMultiDomainDeployment(domains, options = {}) {
|
|
369
|
+
const coordinationId = this.generateOperationId();
|
|
370
|
+
console.log(`🚀 Coordinating deployment across ${domains.length} domains...`);
|
|
371
|
+
console.log(` 🆔 Coordination ID: ${coordinationId}`);
|
|
372
|
+
const coordination = {
|
|
373
|
+
coordinationId,
|
|
374
|
+
startTime: new Date(),
|
|
375
|
+
domains,
|
|
376
|
+
options,
|
|
377
|
+
phases: ['validation', 'preparation', 'deployment', 'verification'],
|
|
378
|
+
currentPhase: 'validation',
|
|
379
|
+
results: {
|
|
380
|
+
successful: [],
|
|
381
|
+
failed: [],
|
|
382
|
+
rolledBack: []
|
|
383
|
+
},
|
|
384
|
+
metrics: {
|
|
385
|
+
totalDomains: domains.length,
|
|
386
|
+
completed: 0,
|
|
387
|
+
failed: 0,
|
|
388
|
+
duration: 0
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
this.session.operations.push(coordination);
|
|
392
|
+
try {
|
|
393
|
+
// Phase 1: Cross-domain validation
|
|
394
|
+
await this.executeCoordinationPhase(coordination, 'validation');
|
|
395
|
+
|
|
396
|
+
// Phase 2: Preparation and dependency resolution
|
|
397
|
+
await this.executeCoordinationPhase(coordination, 'preparation');
|
|
398
|
+
|
|
399
|
+
// Phase 3: Coordinated deployment
|
|
400
|
+
await this.executeCoordinationPhase(coordination, 'deployment');
|
|
401
|
+
|
|
402
|
+
// Phase 4: Cross-domain verification
|
|
403
|
+
await this.executeCoordinationPhase(coordination, 'verification');
|
|
404
|
+
coordination.endTime = new Date();
|
|
405
|
+
coordination.duration = (coordination.endTime - coordination.startTime) / 1000;
|
|
406
|
+
coordination.status = coordination.results.failed.length === 0 ? 'success' : 'partial';
|
|
407
|
+
this.auditCoordinatorEvent('MULTI_DOMAIN_DEPLOYMENT_COMPLETED', {
|
|
408
|
+
coordinationId,
|
|
409
|
+
totalDomains: coordination.metrics.totalDomains,
|
|
410
|
+
successful: coordination.results.successful.length,
|
|
411
|
+
failed: coordination.results.failed.length,
|
|
412
|
+
duration: coordination.duration
|
|
413
|
+
});
|
|
414
|
+
console.log(`✅ Multi-domain deployment completed (${coordination.duration.toFixed(2)}s)`);
|
|
415
|
+
console.log(` ✅ Successful: ${coordination.results.successful.length}`);
|
|
416
|
+
console.log(` ❌ Failed: ${coordination.results.failed.length}`);
|
|
417
|
+
return coordination;
|
|
418
|
+
} catch (error) {
|
|
419
|
+
console.error(`❌ Multi-domain deployment failed: ${error.message}`);
|
|
420
|
+
|
|
421
|
+
// Trigger rollback if enabled
|
|
422
|
+
if (this.config.enableAutoRollback) {
|
|
423
|
+
await this.coordinateRollback(coordination);
|
|
424
|
+
}
|
|
425
|
+
throw error;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Execute specific phase of coordinated deployment
|
|
431
|
+
* @param {Object} coordination - Coordination object
|
|
432
|
+
* @param {string} phaseName - Phase name
|
|
433
|
+
*/
|
|
434
|
+
async executeCoordinationPhase(coordination, phaseName) {
|
|
435
|
+
console.log(`\n📋 Phase: ${phaseName.toUpperCase()}`);
|
|
436
|
+
coordination.currentPhase = phaseName;
|
|
437
|
+
const phaseStartTime = Date.now();
|
|
438
|
+
try {
|
|
439
|
+
switch (phaseName) {
|
|
440
|
+
case 'validation':
|
|
441
|
+
await this.executeCrossValidationPhase(coordination);
|
|
442
|
+
break;
|
|
443
|
+
case 'preparation':
|
|
444
|
+
await this.executePreparationPhase(coordination);
|
|
445
|
+
break;
|
|
446
|
+
case 'deployment':
|
|
447
|
+
await this.executeDeploymentPhase(coordination);
|
|
448
|
+
break;
|
|
449
|
+
case 'verification':
|
|
450
|
+
await this.executeVerificationPhase(coordination);
|
|
451
|
+
break;
|
|
452
|
+
default:
|
|
453
|
+
throw new Error(`Unknown coordination phase: ${phaseName}`);
|
|
454
|
+
}
|
|
455
|
+
const phaseDuration = (Date.now() - phaseStartTime) / 1000;
|
|
456
|
+
console.log(`✅ Phase ${phaseName} completed (${phaseDuration.toFixed(2)}s)`);
|
|
457
|
+
} catch (error) {
|
|
458
|
+
const phaseDuration = (Date.now() - phaseStartTime) / 1000;
|
|
459
|
+
console.error(`❌ Phase ${phaseName} failed (${phaseDuration.toFixed(2)}s): ${error.message}`);
|
|
460
|
+
throw error;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Execute cross-domain validation phase
|
|
466
|
+
* @param {Object} coordination - Coordination object
|
|
467
|
+
*/
|
|
468
|
+
async executeCrossValidationPhase(coordination) {
|
|
469
|
+
console.log(' 🔍 Cross-domain validation...');
|
|
470
|
+
|
|
471
|
+
// Validate each domain
|
|
472
|
+
for (const domain of coordination.domains) {
|
|
473
|
+
try {
|
|
474
|
+
// Individual domain validation
|
|
475
|
+
const validation = await this.modules.validator.validateDeploymentReadiness(domain, {
|
|
476
|
+
environment: coordination.options.environment,
|
|
477
|
+
crossDomainMode: true
|
|
478
|
+
});
|
|
479
|
+
if (!validation.valid) {
|
|
480
|
+
throw new Error(`Validation failed: ${validation.errors.join(', ')}`);
|
|
481
|
+
}
|
|
482
|
+
console.log(` ✅ ${domain}: validation passed`);
|
|
483
|
+
} catch (error) {
|
|
484
|
+
console.error(` ❌ ${domain}: ${error.message}`);
|
|
485
|
+
coordination.results.failed.push({
|
|
486
|
+
domain,
|
|
487
|
+
phase: 'validation',
|
|
488
|
+
error: error.message
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// Cross-domain compatibility checks
|
|
494
|
+
if (this.config.enableCrossValidation) {
|
|
495
|
+
await this.validateCrossDomainCompatibility(coordination);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// Dependency validation
|
|
499
|
+
if (this.config.enableDependencyResolution) {
|
|
500
|
+
await this.validateDependencies(coordination);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Execute preparation phase
|
|
506
|
+
* @param {Object} coordination - Coordination object
|
|
507
|
+
*/
|
|
508
|
+
async executePreparationPhase(coordination) {
|
|
509
|
+
console.log(' 🛠️ Preparation and resource allocation...');
|
|
510
|
+
|
|
511
|
+
// Coordinate shared resources
|
|
512
|
+
if (this.config.enableSharedResources) {
|
|
513
|
+
await this.coordinateSharedResources(coordination);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// Prepare secrets across domains
|
|
517
|
+
await this.prepareSharedSecrets(coordination);
|
|
518
|
+
|
|
519
|
+
// Database coordination
|
|
520
|
+
await this.coordinateDatabases(coordination);
|
|
521
|
+
|
|
522
|
+
// Configuration preparation
|
|
523
|
+
await this.prepareConfigurations(coordination);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Execute deployment phase
|
|
528
|
+
* @param {Object} coordination - Coordination object
|
|
529
|
+
*/
|
|
530
|
+
async executeDeploymentPhase(coordination) {
|
|
531
|
+
console.log(' 🚀 Coordinated deployment execution...');
|
|
532
|
+
|
|
533
|
+
// Resolve deployment order based on dependencies
|
|
534
|
+
const deploymentOrder = this.resolveDependencyOrder(coordination.domains);
|
|
535
|
+
console.log(` 📋 Deployment order: ${deploymentOrder.join(' → ')}`);
|
|
536
|
+
|
|
537
|
+
// Execute deployments in batches
|
|
538
|
+
const batches = this.createDeploymentBatches(deploymentOrder);
|
|
539
|
+
for (let i = 0; i < batches.length; i++) {
|
|
540
|
+
const batch = batches[i];
|
|
541
|
+
console.log(`\n 🔄 Batch ${i + 1}/${batches.length}: ${batch.join(', ')}`);
|
|
542
|
+
await this.executeBatchDeployment(batch, coordination);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Execute verification phase
|
|
548
|
+
* @param {Object} coordination - Coordination object
|
|
549
|
+
*/
|
|
550
|
+
async executeVerificationPhase(coordination) {
|
|
551
|
+
console.log(' ✅ Cross-domain verification...');
|
|
552
|
+
|
|
553
|
+
// Verify each successfully deployed domain
|
|
554
|
+
for (const domain of coordination.results.successful) {
|
|
555
|
+
try {
|
|
556
|
+
const verification = await this.modules.productionTester.runProductionTests(this.getDomainUrl(domain.domain), {
|
|
557
|
+
environment: coordination.options.environment,
|
|
558
|
+
testSuites: ['health', 'endpoints', 'integration']
|
|
559
|
+
});
|
|
560
|
+
if (verification.failed > 0) {
|
|
561
|
+
throw new Error(`${verification.failed} tests failed`);
|
|
562
|
+
}
|
|
563
|
+
console.log(` ✅ ${domain.domain}: verification passed`);
|
|
564
|
+
} catch (error) {
|
|
565
|
+
console.error(` ❌ ${domain.domain}: verification failed - ${error.message}`);
|
|
566
|
+
|
|
567
|
+
// Move from successful to failed
|
|
568
|
+
coordination.results.failed.push({
|
|
569
|
+
domain: domain.domain,
|
|
570
|
+
phase: 'verification',
|
|
571
|
+
error: error.message
|
|
572
|
+
});
|
|
573
|
+
coordination.results.successful = coordination.results.successful.filter(d => d.domain !== domain.domain);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Cross-domain integration tests
|
|
578
|
+
if (coordination.results.successful.length > 1) {
|
|
579
|
+
await this.runCrossDomainIntegrationTests(coordination);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Validate cross-domain compatibility
|
|
585
|
+
* @param {Object} coordination - Coordination object
|
|
586
|
+
*/
|
|
587
|
+
async validateCrossDomainCompatibility(coordination) {
|
|
588
|
+
console.log(' 🔗 Cross-domain compatibility validation...');
|
|
589
|
+
|
|
590
|
+
// Check for version compatibility
|
|
591
|
+
const versions = new Map();
|
|
592
|
+
for (const domain of coordination.domains) {
|
|
593
|
+
const domainInfo = this.portfolio.domains.get(domain);
|
|
594
|
+
if (domainInfo && domainInfo.version) {
|
|
595
|
+
versions.set(domain, domainInfo.version);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// Validate CORS configurations
|
|
600
|
+
await this.validateCorsCompatibility(coordination);
|
|
601
|
+
|
|
602
|
+
// Validate shared service compatibility
|
|
603
|
+
await this.validateSharedServiceCompatibility(coordination);
|
|
604
|
+
console.log(' ✅ Cross-domain compatibility validated');
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Validate CORS compatibility across domains
|
|
609
|
+
* @param {Object} coordination - Coordination object
|
|
610
|
+
*/
|
|
611
|
+
async validateCorsCompatibility(coordination) {
|
|
612
|
+
const corsIssues = [];
|
|
613
|
+
for (const domain of coordination.domains) {
|
|
614
|
+
const domainInfo = this.portfolio.domains.get(domain);
|
|
615
|
+
if (domainInfo && domainInfo.corsOrigins) {
|
|
616
|
+
const corsOrigins = domainInfo.corsOrigins[coordination.options.environment] || [];
|
|
617
|
+
|
|
618
|
+
// Check if domain allows cross-domain requests from other portfolio domains
|
|
619
|
+
for (const otherDomain of coordination.domains) {
|
|
620
|
+
if (domain !== otherDomain) {
|
|
621
|
+
const otherDomainInfo = this.portfolio.domains.get(otherDomain);
|
|
622
|
+
if (otherDomainInfo && otherDomainInfo.domains) {
|
|
623
|
+
const otherUrl = otherDomainInfo.domains[coordination.options.environment];
|
|
624
|
+
const isAllowed = corsOrigins.some(origin => {
|
|
625
|
+
return origin === otherUrl || origin.includes('*') || otherUrl.includes(origin.replace('https://', '').replace('*', ''));
|
|
626
|
+
});
|
|
627
|
+
if (!isAllowed) {
|
|
628
|
+
corsIssues.push(`${domain} does not allow CORS from ${otherDomain} (${otherUrl})`);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
if (corsIssues.length > 0) {
|
|
636
|
+
console.warn(` ⚠️ CORS issues detected:`);
|
|
637
|
+
corsIssues.forEach(issue => console.warn(` - ${issue}`));
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* Coordinate shared resources across domains
|
|
643
|
+
* @param {Object} coordination - Coordination object
|
|
644
|
+
*/
|
|
645
|
+
async coordinateSharedResources(coordination) {
|
|
646
|
+
console.log(' 🤝 Coordinating shared resources...');
|
|
647
|
+
const sharedResources = {
|
|
648
|
+
databases: new Map(),
|
|
649
|
+
secrets: new Map(),
|
|
650
|
+
configurations: new Map()
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
// Identify shared resources
|
|
654
|
+
for (const domain of coordination.domains) {
|
|
655
|
+
const domainInfo = this.portfolio.domains.get(domain);
|
|
656
|
+
if (domainInfo) {
|
|
657
|
+
// Check for shared databases
|
|
658
|
+
if (domainInfo.databases) {
|
|
659
|
+
for (const [env, dbConfig] of Object.entries(domainInfo.databases)) {
|
|
660
|
+
if (dbConfig.shared) {
|
|
661
|
+
const resourceKey = `${dbConfig.name}-${env}`;
|
|
662
|
+
if (!sharedResources.databases.has(resourceKey)) {
|
|
663
|
+
sharedResources.databases.set(resourceKey, {
|
|
664
|
+
config: dbConfig,
|
|
665
|
+
domains: []
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
sharedResources.databases.get(resourceKey).domains.push(domain);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
this.portfolio.sharedResources = sharedResources;
|
|
675
|
+
console.log(` ✅ Found ${sharedResources.databases.size} shared databases`);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* Prepare shared secrets across domains
|
|
680
|
+
* @param {Object} coordination - Coordination object
|
|
681
|
+
*/
|
|
682
|
+
async prepareSharedSecrets(coordination) {
|
|
683
|
+
console.log(' 🔐 Preparing shared secrets...');
|
|
684
|
+
const sharedSecrets = await this.modules.secretManager.coordinateSecretsAcrossEnvironments(coordination.domains, coordination.options.environment, {
|
|
685
|
+
syncCriticalSecrets: true,
|
|
686
|
+
generateUniquePerDomain: false
|
|
687
|
+
});
|
|
688
|
+
console.log(` ✅ Coordinated secrets for ${coordination.domains.length} domains`);
|
|
689
|
+
return sharedSecrets;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* Coordinate databases across domains
|
|
694
|
+
* @param {Object} coordination - Coordination object
|
|
695
|
+
*/
|
|
696
|
+
async coordinateDatabases(coordination) {
|
|
697
|
+
console.log(' 🗄️ Coordinating databases...');
|
|
698
|
+
for (const [resourceKey, resource] of this.portfolio.sharedResources.databases) {
|
|
699
|
+
console.log(` 📊 Shared database: ${resourceKey} (used by ${resource.domains.join(', ')})`);
|
|
700
|
+
|
|
701
|
+
// Ensure shared database is ready for all domains
|
|
702
|
+
await this.modules.databaseOrchestrator.coordinateSharedDatabase(resource.config, resource.domains, coordination.options.environment);
|
|
703
|
+
}
|
|
704
|
+
console.log(' ✅ Database coordination completed');
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* Prepare configurations for all domains
|
|
709
|
+
* @param {Object} coordination - Coordination object
|
|
710
|
+
*/
|
|
711
|
+
async prepareConfigurations(coordination) {
|
|
712
|
+
console.log(' ⚙️ Preparing configurations...');
|
|
713
|
+
for (const domain of coordination.domains) {
|
|
714
|
+
try {
|
|
715
|
+
await this.modules.configCache.getOrCreateDomainConfig(domain, {
|
|
716
|
+
environment: coordination.options.environment,
|
|
717
|
+
forceRefresh: coordination.options.forceRefresh || false
|
|
718
|
+
});
|
|
719
|
+
console.log(` ✅ ${domain}: configuration ready`);
|
|
720
|
+
} catch (error) {
|
|
721
|
+
console.error(` ❌ ${domain}: configuration failed - ${error.message}`);
|
|
722
|
+
coordination.results.failed.push({
|
|
723
|
+
domain,
|
|
724
|
+
phase: 'configuration',
|
|
725
|
+
error: error.message
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
/**
|
|
732
|
+
* Resolve deployment order based on dependencies
|
|
733
|
+
* @param {Array} domains - Domains to deploy
|
|
734
|
+
* @returns {Array} Ordered domains for deployment
|
|
735
|
+
*/
|
|
736
|
+
resolveDependencyOrder(domains) {
|
|
737
|
+
const ordered = [];
|
|
738
|
+
const visited = new Set();
|
|
739
|
+
const visiting = new Set();
|
|
740
|
+
const visit = domain => {
|
|
741
|
+
if (visiting.has(domain)) {
|
|
742
|
+
throw new Error(`Circular dependency detected: ${domain}`);
|
|
743
|
+
}
|
|
744
|
+
if (visited.has(domain)) {
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
visiting.add(domain);
|
|
748
|
+
const dependencies = this.portfolio.dependencies.get(domain) || [];
|
|
749
|
+
for (const dep of dependencies) {
|
|
750
|
+
if (domains.includes(dep)) {
|
|
751
|
+
visit(dep);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
visiting.delete(domain);
|
|
755
|
+
visited.add(domain);
|
|
756
|
+
ordered.push(domain);
|
|
757
|
+
};
|
|
758
|
+
domains.forEach(domain => {
|
|
759
|
+
if (!visited.has(domain)) {
|
|
760
|
+
visit(domain);
|
|
761
|
+
}
|
|
762
|
+
});
|
|
763
|
+
return ordered;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* Create deployment batches for parallel execution
|
|
768
|
+
* @param {Array} orderedDomains - Domains in dependency order
|
|
769
|
+
* @returns {Array} Batches of domains
|
|
770
|
+
*/
|
|
771
|
+
createDeploymentBatches(orderedDomains) {
|
|
772
|
+
const batches = [];
|
|
773
|
+
const batchSize = this.config.maxConcurrentDeployments;
|
|
774
|
+
for (let i = 0; i < orderedDomains.length; i += batchSize) {
|
|
775
|
+
batches.push(orderedDomains.slice(i, i + batchSize));
|
|
776
|
+
}
|
|
777
|
+
return batches;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* Execute batch deployment
|
|
782
|
+
* @param {Array} batch - Domains in batch
|
|
783
|
+
* @param {Object} coordination - Coordination object
|
|
784
|
+
*/
|
|
785
|
+
async executeBatchDeployment(batch, coordination) {
|
|
786
|
+
const batchPromises = batch.map(async domain => {
|
|
787
|
+
try {
|
|
788
|
+
console.log(` 🚀 Deploying ${domain}...`);
|
|
789
|
+
const deployResult = await this.modules.orchestrator.deployDomain(domain, {
|
|
790
|
+
environment: coordination.options.environment,
|
|
791
|
+
skipValidation: true,
|
|
792
|
+
// Already validated
|
|
793
|
+
coordinationMode: true
|
|
794
|
+
});
|
|
795
|
+
coordination.results.successful.push({
|
|
796
|
+
domain,
|
|
797
|
+
result: deployResult
|
|
798
|
+
});
|
|
799
|
+
coordination.metrics.completed++;
|
|
800
|
+
console.log(` ✅ ${domain}: deployment successful`);
|
|
801
|
+
return {
|
|
802
|
+
domain,
|
|
803
|
+
status: 'success',
|
|
804
|
+
result: deployResult
|
|
805
|
+
};
|
|
806
|
+
} catch (error) {
|
|
807
|
+
coordination.results.failed.push({
|
|
808
|
+
domain,
|
|
809
|
+
phase: 'deployment',
|
|
810
|
+
error: error.message
|
|
811
|
+
});
|
|
812
|
+
coordination.metrics.failed++;
|
|
813
|
+
console.error(` ❌ ${domain}: deployment failed - ${error.message}`);
|
|
814
|
+
return {
|
|
815
|
+
domain,
|
|
816
|
+
status: 'failed',
|
|
817
|
+
error: error.message
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
});
|
|
821
|
+
await Promise.all(batchPromises);
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
/**
|
|
825
|
+
* Coordinate rollback across failed deployments
|
|
826
|
+
* @param {Object} coordination - Coordination object
|
|
827
|
+
*/
|
|
828
|
+
async coordinateRollback(coordination) {
|
|
829
|
+
console.log('\n🔄 Coordinating cross-domain rollback...');
|
|
830
|
+
const rollbackId = this.generateOperationId();
|
|
831
|
+
this.auditCoordinatorEvent('CROSS_DOMAIN_ROLLBACK_START', {
|
|
832
|
+
coordinationId: coordination.coordinationId,
|
|
833
|
+
rollbackId,
|
|
834
|
+
domainsToRollback: coordination.results.successful.length
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
// Rollback successful deployments in reverse order
|
|
838
|
+
const rollbackOrder = [...coordination.results.successful].reverse();
|
|
839
|
+
for (const deployment of rollbackOrder) {
|
|
840
|
+
try {
|
|
841
|
+
console.log(` 🔄 Rolling back ${deployment.domain}...`);
|
|
842
|
+
await this.modules.rollbackManager.executeRollback(deployment.domain, {
|
|
843
|
+
deploymentId: deployment.result?.deploymentId,
|
|
844
|
+
reason: 'cross-domain-failure',
|
|
845
|
+
coordinationId: coordination.coordinationId
|
|
846
|
+
});
|
|
847
|
+
coordination.results.rolledBack.push(deployment);
|
|
848
|
+
console.log(` ✅ ${deployment.domain}: rollback completed`);
|
|
849
|
+
} catch (error) {
|
|
850
|
+
console.error(` ❌ ${deployment.domain}: rollback failed - ${error.message}`);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
this.auditCoordinatorEvent('CROSS_DOMAIN_ROLLBACK_COMPLETED', {
|
|
854
|
+
coordinationId: coordination.coordinationId,
|
|
855
|
+
rollbackId,
|
|
856
|
+
rolledBackDomains: coordination.results.rolledBack.length
|
|
857
|
+
});
|
|
858
|
+
console.log(`✅ Cross-domain rollback completed: ${coordination.results.rolledBack.length} domains`);
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
/**
|
|
862
|
+
* Run cross-domain integration tests
|
|
863
|
+
*/
|
|
864
|
+
async runCrossDomainIntegrationTests() {
|
|
865
|
+
console.log(' 🔗 Running cross-domain integration tests...');
|
|
866
|
+
const integrationTests = ['cross-domain-cors', 'shared-resource-access', 'inter-domain-communication'];
|
|
867
|
+
for (const testName of integrationTests) {
|
|
868
|
+
try {
|
|
869
|
+
console.log(` 🧪 ${testName}...`);
|
|
870
|
+
|
|
871
|
+
// Simulate integration test
|
|
872
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
873
|
+
console.log(` ✅ ${testName}: passed`);
|
|
874
|
+
} catch (error) {
|
|
875
|
+
console.error(` ❌ ${testName}: failed - ${error.message}`);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/**
|
|
881
|
+
* Monitor portfolio health continuously
|
|
882
|
+
* @returns {Promise<Object>} Health monitoring results
|
|
883
|
+
*/
|
|
884
|
+
async monitorPortfolioHealth() {
|
|
885
|
+
if (!this.config.enableContinuousMonitoring) {
|
|
886
|
+
console.log('📊 Portfolio health monitoring is disabled');
|
|
887
|
+
return {
|
|
888
|
+
enabled: false
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
console.log('📊 Starting portfolio health monitoring...');
|
|
892
|
+
const monitoringSession = {
|
|
893
|
+
sessionId: this.generateOperationId(),
|
|
894
|
+
startTime: new Date(),
|
|
895
|
+
domains: Array.from(this.portfolio.domains.keys()),
|
|
896
|
+
healthChecks: []
|
|
897
|
+
};
|
|
898
|
+
const healthPromises = monitoringSession.domains.map(async domain => {
|
|
899
|
+
try {
|
|
900
|
+
const healthResult = await this.modules.productionTester.quickHealthCheck(this.getDomainUrl(domain));
|
|
901
|
+
const healthStatus = {
|
|
902
|
+
domain,
|
|
903
|
+
status: healthResult.failed === 0 ? 'healthy' : 'unhealthy',
|
|
904
|
+
timestamp: new Date(),
|
|
905
|
+
details: healthResult
|
|
906
|
+
};
|
|
907
|
+
this.portfolio.healthStatus.set(domain, healthStatus);
|
|
908
|
+
monitoringSession.healthChecks.push(healthStatus);
|
|
909
|
+
return healthStatus;
|
|
910
|
+
} catch (error) {
|
|
911
|
+
const errorStatus = {
|
|
912
|
+
domain,
|
|
913
|
+
status: 'error',
|
|
914
|
+
timestamp: new Date(),
|
|
915
|
+
error: error.message
|
|
916
|
+
};
|
|
917
|
+
this.portfolio.healthStatus.set(domain, errorStatus);
|
|
918
|
+
monitoringSession.healthChecks.push(errorStatus);
|
|
919
|
+
return errorStatus;
|
|
920
|
+
}
|
|
921
|
+
});
|
|
922
|
+
await Promise.all(healthPromises);
|
|
923
|
+
const healthySummary = {
|
|
924
|
+
total: monitoringSession.domains.length,
|
|
925
|
+
healthy: monitoringSession.healthChecks.filter(h => h.status === 'healthy').length,
|
|
926
|
+
unhealthy: monitoringSession.healthChecks.filter(h => h.status === 'unhealthy').length,
|
|
927
|
+
errors: monitoringSession.healthChecks.filter(h => h.status === 'error').length
|
|
928
|
+
};
|
|
929
|
+
console.log(`📊 Portfolio health: ${healthySummary.healthy}/${healthySummary.total} healthy`);
|
|
930
|
+
return {
|
|
931
|
+
session: monitoringSession,
|
|
932
|
+
summary: healthySummary,
|
|
933
|
+
details: monitoringSession.healthChecks
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
/**
|
|
938
|
+
* Get domain URL for testing
|
|
939
|
+
* @param {string} domain - Domain name
|
|
940
|
+
* @returns {string} Domain URL
|
|
941
|
+
*/
|
|
942
|
+
getDomainUrl(domain) {
|
|
943
|
+
const domainInfo = this.portfolio.domains.get(domain);
|
|
944
|
+
if (domainInfo && domainInfo.services && domainInfo.services.api) {
|
|
945
|
+
return domainInfo.services.api.production || domainInfo.services.api[this.config.defaultEnvironment];
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
// Fallback to default pattern
|
|
949
|
+
return `https://${domain.replace(/\./g, '')}-data-service.tamylatrading.workers.dev`;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
/**
|
|
953
|
+
* Generate unique operation ID
|
|
954
|
+
* @returns {string} Operation ID
|
|
955
|
+
*/
|
|
956
|
+
generateOperationId() {
|
|
957
|
+
const timestamp = Date.now().toString(36);
|
|
958
|
+
const random = Math.random().toString(36).substr(2, 9);
|
|
959
|
+
return `op_${timestamp}_${random}`;
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
/**
|
|
963
|
+
* Generate unique session ID
|
|
964
|
+
* @returns {string} Session ID
|
|
965
|
+
*/
|
|
966
|
+
generateSessionId() {
|
|
967
|
+
const timestamp = Date.now().toString(36);
|
|
968
|
+
const random = Math.random().toString(36).substr(2, 9);
|
|
969
|
+
return `coord_${timestamp}_${random}`;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
/**
|
|
973
|
+
* Audit coordinator events
|
|
974
|
+
* @param {string} event - Event type
|
|
975
|
+
* @param {Object} details - Event details
|
|
976
|
+
*/
|
|
977
|
+
auditCoordinatorEvent(event, details = {}) {
|
|
978
|
+
if (this.modules.auditor) {
|
|
979
|
+
this.modules.auditor.logAuditEvent(event, 'COORDINATOR', {
|
|
980
|
+
sessionId: this.session.sessionId,
|
|
981
|
+
portfolioName: this.config.portfolioName,
|
|
982
|
+
...details
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
/**
|
|
988
|
+
* Get portfolio statistics
|
|
989
|
+
* @returns {Object} Portfolio statistics
|
|
990
|
+
*/
|
|
991
|
+
getPortfolioStatistics() {
|
|
992
|
+
return {
|
|
993
|
+
portfolio: {
|
|
994
|
+
name: this.config.portfolioName,
|
|
995
|
+
sessionId: this.session.sessionId,
|
|
996
|
+
totalDomains: this.portfolio.metrics.totalDomains,
|
|
997
|
+
activeDomains: Array.from(this.portfolio.domains.keys()),
|
|
998
|
+
sharedResources: Array.from(this.portfolio.sharedResources.entries()),
|
|
999
|
+
dependencies: Array.from(this.portfolio.dependencies.entries())
|
|
1000
|
+
},
|
|
1001
|
+
health: {
|
|
1002
|
+
monitored: this.portfolio.healthStatus.size,
|
|
1003
|
+
healthy: Array.from(this.portfolio.healthStatus.values()).filter(h => h.status === 'healthy').length,
|
|
1004
|
+
unhealthy: Array.from(this.portfolio.healthStatus.values()).filter(h => h.status !== 'healthy').length
|
|
1005
|
+
},
|
|
1006
|
+
deployments: {
|
|
1007
|
+
total: this.portfolio.metrics.completedDeployments + this.portfolio.metrics.failedDeployments,
|
|
1008
|
+
completed: this.portfolio.metrics.completedDeployments,
|
|
1009
|
+
failed: this.portfolio.metrics.failedDeployments,
|
|
1010
|
+
rolledBack: this.portfolio.metrics.rolledBackDeployments
|
|
1011
|
+
}
|
|
1012
|
+
};
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
/**
|
|
1016
|
+
* Validate shared service compatibility
|
|
1017
|
+
*/
|
|
1018
|
+
async validateSharedServiceCompatibility() {
|
|
1019
|
+
// Implementation for shared service validation
|
|
1020
|
+
console.log(' 🔗 Validating shared service compatibility...');
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
/**
|
|
1024
|
+
* Validate dependencies
|
|
1025
|
+
*/
|
|
1026
|
+
async validateDependencies() {
|
|
1027
|
+
// Implementation for dependency validation
|
|
1028
|
+
console.log(' 📋 Validating domain dependencies...');
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
// Legacy function exports for backward compatibility
|
|
1033
|
+
|
|
1034
|
+
/**
|
|
1035
|
+
* Simple cross-domain deployment
|
|
1036
|
+
* @param {Array} domains - Domains to deploy
|
|
1037
|
+
* @param {Object} options - Deployment options
|
|
1038
|
+
* @returns {Promise<Object>} Deployment results
|
|
1039
|
+
*/
|
|
1040
|
+
export async function deployMultipleDomains(domains, options = {}) {
|
|
1041
|
+
const coordinator = new CrossDomainCoordinator();
|
|
1042
|
+
|
|
1043
|
+
// Auto-discover if not already done
|
|
1044
|
+
await coordinator.discoverPortfolio(domains);
|
|
1045
|
+
return await coordinator.coordinateMultiDomainDeployment(domains, options);
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
/**
|
|
1049
|
+
* Monitor portfolio health
|
|
1050
|
+
* @param {Array} domains - Domains to monitor
|
|
1051
|
+
* @returns {Promise<Object>} Health monitoring results
|
|
1052
|
+
*/
|
|
1053
|
+
export async function monitorDomainPortfolio(domains = []) {
|
|
1054
|
+
const coordinator = new CrossDomainCoordinator({
|
|
1055
|
+
enableContinuousMonitoring: true
|
|
1056
|
+
});
|
|
1057
|
+
if (domains.length > 0) {
|
|
1058
|
+
await coordinator.discoverPortfolio(domains);
|
|
1059
|
+
} else {
|
|
1060
|
+
await coordinator.discoverPortfolio();
|
|
1061
|
+
}
|
|
1062
|
+
return await coordinator.monitorPortfolioHealth();
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
/**
|
|
1066
|
+
* Coordinate shared resources across domains
|
|
1067
|
+
* @param {Array} domains - Domains to coordinate
|
|
1068
|
+
* @param {Object} sharedConfig - Shared resource configuration
|
|
1069
|
+
* @returns {Promise<Object>} Coordination results
|
|
1070
|
+
*/
|
|
1071
|
+
export async function coordinateSharedResources(domains, sharedConfig = {}) {
|
|
1072
|
+
const coordinator = new CrossDomainCoordinator({
|
|
1073
|
+
enableSharedResources: true
|
|
1074
|
+
});
|
|
1075
|
+
await coordinator.discoverPortfolio(domains);
|
|
1076
|
+
|
|
1077
|
+
// This would implement actual shared resource coordination
|
|
1078
|
+
return {
|
|
1079
|
+
domains: domains.length,
|
|
1080
|
+
sharedResources: Object.keys(sharedConfig).length,
|
|
1081
|
+
status: 'coordinated'
|
|
1082
|
+
};
|
|
1083
|
+
}
|