@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.
Files changed (169) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +53 -0
  3. package/dist/bin/clodo-service.js +47 -15
  4. package/dist/bin/commands/deploy.js +115 -83
  5. package/dist/bin/commands/helpers/deployment-ui.js +138 -0
  6. package/dist/bin/commands/helpers/deployment-verification.js +251 -0
  7. package/dist/bin/commands/helpers/error-recovery.js +80 -0
  8. package/dist/bin/commands/helpers/resource-detection.js +113 -0
  9. package/dist/bin/commands/validate.js +1 -1
  10. package/dist/bin/security/security-cli.js +1 -1
  11. package/dist/bin/shared/cache/configuration-cache.js +82 -0
  12. package/dist/bin/shared/cloudflare/domain-manager.js +1 -1
  13. package/dist/bin/shared/cloudflare/index.js +1 -1
  14. package/dist/bin/shared/cloudflare/ops.js +6 -4
  15. package/dist/bin/shared/config/ConfigurationManager.js +23 -1
  16. package/dist/bin/shared/config/command-config-manager.js +19 -3
  17. package/dist/bin/shared/config/index.js +1 -1
  18. package/dist/bin/shared/deployment/credential-collector.js +30 -7
  19. package/dist/bin/shared/deployment/index.js +2 -2
  20. package/dist/bin/shared/deployment/rollback-manager.js +4 -520
  21. package/dist/bin/shared/deployment/utilities/d1-error-recovery.js +177 -0
  22. package/dist/bin/shared/deployment/validator.js +40 -10
  23. package/dist/bin/shared/deployment/workflows/deployment-summary.js +214 -0
  24. package/dist/bin/shared/deployment/workflows/interactive-confirmation.js +188 -0
  25. package/dist/bin/shared/deployment/workflows/interactive-database-workflow.js +234 -0
  26. package/dist/bin/shared/deployment/workflows/interactive-domain-info-gatherer.js +240 -0
  27. package/dist/bin/shared/deployment/workflows/interactive-secret-workflow.js +228 -0
  28. package/dist/bin/shared/deployment/workflows/interactive-testing-workflow.js +235 -0
  29. package/dist/bin/shared/deployment/workflows/interactive-validation.js +218 -0
  30. package/dist/bin/shared/error-handling/error-classifier.js +46 -0
  31. package/dist/bin/shared/monitoring/health-checker.js +129 -1
  32. package/dist/bin/shared/monitoring/memory-manager.js +17 -6
  33. package/dist/bin/shared/routing/domain-router.js +1 -1
  34. package/dist/bin/shared/utils/deployment-validator.js +97 -0
  35. package/dist/bin/shared/utils/formatters.js +10 -0
  36. package/dist/bin/shared/utils/index.js +13 -1
  37. package/dist/bin/shared/utils/interactive-prompts.js +34 -18
  38. package/dist/bin/shared/utils/progress-manager.js +2 -2
  39. package/dist/bin/shared/utils/progress-spinner.js +53 -0
  40. package/dist/bin/shared/utils/sensitive-redactor.js +91 -0
  41. package/dist/bin/shared/validation/ValidationRegistry.js +1 -1
  42. package/dist/security/index.js +1 -1
  43. package/dist/security/patterns/insecure-patterns.js +1 -1
  44. package/dist/utils/constants.js +102 -0
  45. package/dist/utils/deployment/wrangler-config-manager.js +215 -48
  46. package/dist/utils/framework-config.js +2 -2
  47. package/dist/utils/interactive-prompts.js +10 -59
  48. package/package.json +16 -8
  49. package/dist/bin/clodo-service-old.js +0 -868
  50. package/dist/bin/clodo-service-test.js +0 -10
  51. package/dist/bin/commands/assess.js +0 -91
  52. package/dist/bin/commands/create.js +0 -77
  53. package/dist/bin/commands/diagnose.js +0 -83
  54. package/dist/bin/commands/helpers.js +0 -138
  55. package/dist/bin/commands/update.js +0 -75
  56. package/dist/bin/database/deployment-db-manager.js +0 -423
  57. package/dist/bin/database/enterprise-db-manager.js +0 -457
  58. package/dist/bin/database/wrangler-d1-manager.js +0 -685
  59. package/dist/bin/deployment/enterprise-deploy.js +0 -877
  60. package/dist/bin/deployment/master-deploy.js +0 -1376
  61. package/dist/bin/deployment/modular-enterprise-deploy.js +0 -466
  62. package/dist/bin/deployment/modules/DeploymentConfiguration.js +0 -395
  63. package/dist/bin/deployment/modules/DeploymentOrchestrator.js +0 -492
  64. package/dist/bin/deployment/modules/EnvironmentManager.js +0 -517
  65. package/dist/bin/deployment/modules/MonitoringIntegration.js +0 -560
  66. package/dist/bin/deployment/modules/ValidationManager.js +0 -342
  67. package/dist/bin/deployment/orchestration/BaseDeploymentOrchestrator.js +0 -426
  68. package/dist/bin/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
  69. package/dist/bin/deployment/orchestration/PortfolioOrchestrator.js +0 -273
  70. package/dist/bin/deployment/orchestration/SingleServiceOrchestrator.js +0 -231
  71. package/dist/bin/deployment/orchestration/UnifiedDeploymentOrchestrator.js +0 -662
  72. package/dist/bin/deployment/test-interactive-utils.js +0 -66
  73. package/dist/bin/portfolio/portfolio-manager.js +0 -487
  74. package/dist/bin/service-management/create-service.js +0 -122
  75. package/dist/bin/service-management/init-service.js +0 -79
  76. package/dist/config/customers.js +0 -623
  77. package/dist/config/domains.js +0 -186
  78. package/dist/config/index.js +0 -6
  79. package/dist/database/database-orchestrator.js +0 -795
  80. package/dist/database/index.js +0 -4
  81. package/dist/deployment/index.js +0 -11
  82. package/dist/deployment/orchestration/BaseDeploymentOrchestrator.js +0 -426
  83. package/dist/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
  84. package/dist/deployment/orchestration/PortfolioOrchestrator.js +0 -273
  85. package/dist/deployment/orchestration/SingleServiceOrchestrator.js +0 -231
  86. package/dist/deployment/orchestration/UnifiedDeploymentOrchestrator.js +0 -662
  87. package/dist/deployment/orchestration/index.js +0 -17
  88. package/dist/deployment/rollback-manager.js +0 -36
  89. package/dist/deployment/wrangler-deployer.js +0 -640
  90. package/dist/handlers/GenericRouteHandler.js +0 -532
  91. package/dist/migration/MigrationAdapters.js +0 -562
  92. package/dist/modules/ModuleManager.js +0 -668
  93. package/dist/modules/security.js +0 -96
  94. package/dist/orchestration/cross-domain-coordinator.js +0 -1083
  95. package/dist/orchestration/index.js +0 -5
  96. package/dist/orchestration/modules/DeploymentCoordinator.js +0 -368
  97. package/dist/orchestration/modules/DomainResolver.js +0 -198
  98. package/dist/orchestration/modules/StateManager.js +0 -332
  99. package/dist/orchestration/multi-domain-orchestrator.js +0 -724
  100. package/dist/routing/EnhancedRouter.js +0 -158
  101. package/dist/schema/SchemaManager.js +0 -778
  102. package/dist/service-management/ConfirmationEngine.js +0 -412
  103. package/dist/service-management/ErrorTracker.js +0 -299
  104. package/dist/service-management/GenerationEngine.js +0 -447
  105. package/dist/service-management/InputCollector.js +0 -619
  106. package/dist/service-management/ServiceCreator.js +0 -265
  107. package/dist/service-management/ServiceInitializer.js +0 -453
  108. package/dist/service-management/ServiceOrchestrator.js +0 -633
  109. package/dist/service-management/generators/BaseGenerator.js +0 -233
  110. package/dist/service-management/generators/GeneratorRegistry.js +0 -254
  111. package/dist/service-management/generators/cicd/CiWorkflowGenerator.js +0 -87
  112. package/dist/service-management/generators/cicd/DeployWorkflowGenerator.js +0 -106
  113. package/dist/service-management/generators/code/ServiceHandlersGenerator.js +0 -235
  114. package/dist/service-management/generators/code/ServiceMiddlewareGenerator.js +0 -116
  115. package/dist/service-management/generators/code/ServiceUtilsGenerator.js +0 -246
  116. package/dist/service-management/generators/code/WorkerIndexGenerator.js +0 -143
  117. package/dist/service-management/generators/config/DevelopmentEnvGenerator.js +0 -101
  118. package/dist/service-management/generators/config/DomainsConfigGenerator.js +0 -175
  119. package/dist/service-management/generators/config/EnvExampleGenerator.js +0 -178
  120. package/dist/service-management/generators/config/ProductionEnvGenerator.js +0 -97
  121. package/dist/service-management/generators/config/StagingEnvGenerator.js +0 -97
  122. package/dist/service-management/generators/config/WranglerTomlGenerator.js +0 -238
  123. package/dist/service-management/generators/core/PackageJsonGenerator.js +0 -243
  124. package/dist/service-management/generators/core/SiteConfigGenerator.js +0 -115
  125. package/dist/service-management/generators/documentation/ApiDocsGenerator.js +0 -331
  126. package/dist/service-management/generators/documentation/ConfigurationDocsGenerator.js +0 -294
  127. package/dist/service-management/generators/documentation/DeploymentDocsGenerator.js +0 -244
  128. package/dist/service-management/generators/documentation/ReadmeGenerator.js +0 -196
  129. package/dist/service-management/generators/schemas/ServiceSchemaGenerator.js +0 -190
  130. package/dist/service-management/generators/scripts/DeployScriptGenerator.js +0 -123
  131. package/dist/service-management/generators/scripts/HealthCheckScriptGenerator.js +0 -101
  132. package/dist/service-management/generators/scripts/SetupScriptGenerator.js +0 -88
  133. package/dist/service-management/generators/service-types/StaticSiteGenerator.js +0 -342
  134. package/dist/service-management/generators/testing/EslintConfigGenerator.js +0 -85
  135. package/dist/service-management/generators/testing/IntegrationTestsGenerator.js +0 -237
  136. package/dist/service-management/generators/testing/JestConfigGenerator.js +0 -72
  137. package/dist/service-management/generators/testing/UnitTestsGenerator.js +0 -277
  138. package/dist/service-management/generators/tooling/DockerComposeGenerator.js +0 -71
  139. package/dist/service-management/generators/tooling/GitignoreGenerator.js +0 -143
  140. package/dist/service-management/generators/utils/FileWriter.js +0 -179
  141. package/dist/service-management/generators/utils/PathResolver.js +0 -157
  142. package/dist/service-management/generators/utils/ServiceManifestGenerator.js +0 -111
  143. package/dist/service-management/generators/utils/TemplateEngine.js +0 -185
  144. package/dist/service-management/generators/utils/index.js +0 -18
  145. package/dist/service-management/handlers/ConfirmationHandler.js +0 -71
  146. package/dist/service-management/handlers/GenerationHandler.js +0 -80
  147. package/dist/service-management/handlers/InputHandler.js +0 -59
  148. package/dist/service-management/handlers/ValidationHandler.js +0 -203
  149. package/dist/service-management/index.js +0 -14
  150. package/dist/service-management/routing/DomainRouteMapper.js +0 -311
  151. package/dist/service-management/routing/RouteGenerator.js +0 -266
  152. package/dist/service-management/routing/WranglerRoutesBuilder.js +0 -273
  153. package/dist/service-management/routing/index.js +0 -14
  154. package/dist/service-management/services/DirectoryStructureService.js +0 -56
  155. package/dist/service-management/services/GenerationCoordinator.js +0 -208
  156. package/dist/service-management/services/GeneratorRegistry.js +0 -174
  157. package/dist/services/GenericDataService.js +0 -501
  158. package/dist/ui-structures/concepts/second-order-acquisition-strategy.md +0 -286
  159. package/dist/ui-structures/concepts/service-lifecycle-management.md +0 -150
  160. package/dist/ui-structures/concepts/service-manifest-guide.md +0 -309
  161. package/dist/ui-structures/concepts/three-tier-categorization-strategy.md +0 -231
  162. package/dist/ui-structures/creation/automated-generation-ui.json +0 -246
  163. package/dist/ui-structures/creation/core-inputs-ui.json +0 -217
  164. package/dist/ui-structures/creation/smart-confirmable-ui.json +0 -451
  165. package/dist/ui-structures/reference/absolutely-required-inputs.json +0 -315
  166. package/dist/ui-structures/reference/service-manifest-template.json +0 -342
  167. package/dist/version/VersionDetector.js +0 -723
  168. package/dist/worker/index.js +0 -4
  169. 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;