@tamyla/clodo-framework 3.1.21 → 3.1.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/README.md +53 -0
- package/dist/bin/clodo-service.js +47 -15
- package/dist/bin/commands/deploy.js +115 -83
- package/dist/bin/commands/helpers/deployment-ui.js +138 -0
- package/dist/bin/commands/helpers/deployment-verification.js +251 -0
- package/dist/bin/commands/helpers/error-recovery.js +80 -0
- package/dist/bin/commands/helpers/resource-detection.js +113 -0
- package/dist/bin/commands/validate.js +1 -1
- package/dist/bin/security/security-cli.js +1 -1
- package/dist/bin/shared/cache/configuration-cache.js +82 -0
- package/dist/bin/shared/cloudflare/domain-manager.js +1 -1
- package/dist/bin/shared/cloudflare/index.js +1 -1
- package/dist/bin/shared/cloudflare/ops.js +6 -4
- package/dist/bin/shared/config/ConfigurationManager.js +23 -1
- package/dist/bin/shared/config/command-config-manager.js +19 -3
- package/dist/bin/shared/config/index.js +1 -1
- package/dist/bin/shared/deployment/credential-collector.js +30 -7
- package/dist/bin/shared/deployment/index.js +2 -2
- package/dist/bin/shared/deployment/rollback-manager.js +4 -520
- package/dist/bin/shared/deployment/utilities/d1-error-recovery.js +177 -0
- package/dist/bin/shared/deployment/validator.js +40 -10
- package/dist/bin/shared/deployment/workflows/deployment-summary.js +214 -0
- package/dist/bin/shared/deployment/workflows/interactive-confirmation.js +188 -0
- package/dist/bin/shared/deployment/workflows/interactive-database-workflow.js +234 -0
- package/dist/bin/shared/deployment/workflows/interactive-domain-info-gatherer.js +240 -0
- package/dist/bin/shared/deployment/workflows/interactive-secret-workflow.js +228 -0
- package/dist/bin/shared/deployment/workflows/interactive-testing-workflow.js +235 -0
- package/dist/bin/shared/deployment/workflows/interactive-validation.js +218 -0
- package/dist/bin/shared/error-handling/error-classifier.js +46 -0
- package/dist/bin/shared/monitoring/health-checker.js +129 -1
- package/dist/bin/shared/monitoring/memory-manager.js +17 -6
- package/dist/bin/shared/routing/domain-router.js +1 -1
- package/dist/bin/shared/utils/deployment-validator.js +97 -0
- package/dist/bin/shared/utils/formatters.js +10 -0
- package/dist/bin/shared/utils/index.js +13 -1
- package/dist/bin/shared/utils/interactive-prompts.js +34 -18
- package/dist/bin/shared/utils/progress-manager.js +2 -2
- package/dist/bin/shared/utils/progress-spinner.js +53 -0
- package/dist/bin/shared/utils/sensitive-redactor.js +91 -0
- package/dist/bin/shared/validation/ValidationRegistry.js +1 -1
- package/dist/security/index.js +1 -1
- package/dist/security/patterns/insecure-patterns.js +1 -1
- package/dist/utils/constants.js +102 -0
- package/dist/utils/deployment/wrangler-config-manager.js +215 -48
- package/dist/utils/framework-config.js +2 -2
- package/dist/utils/interactive-prompts.js +10 -59
- package/package.json +16 -8
- package/dist/bin/clodo-service-old.js +0 -868
- package/dist/bin/clodo-service-test.js +0 -10
- package/dist/bin/commands/assess.js +0 -91
- package/dist/bin/commands/create.js +0 -77
- package/dist/bin/commands/diagnose.js +0 -83
- package/dist/bin/commands/helpers.js +0 -138
- package/dist/bin/commands/update.js +0 -75
- package/dist/bin/database/deployment-db-manager.js +0 -423
- package/dist/bin/database/enterprise-db-manager.js +0 -457
- package/dist/bin/database/wrangler-d1-manager.js +0 -685
- package/dist/bin/deployment/enterprise-deploy.js +0 -877
- package/dist/bin/deployment/master-deploy.js +0 -1376
- package/dist/bin/deployment/modular-enterprise-deploy.js +0 -466
- package/dist/bin/deployment/modules/DeploymentConfiguration.js +0 -395
- package/dist/bin/deployment/modules/DeploymentOrchestrator.js +0 -492
- package/dist/bin/deployment/modules/EnvironmentManager.js +0 -517
- package/dist/bin/deployment/modules/MonitoringIntegration.js +0 -560
- package/dist/bin/deployment/modules/ValidationManager.js +0 -342
- package/dist/bin/deployment/orchestration/BaseDeploymentOrchestrator.js +0 -426
- package/dist/bin/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
- package/dist/bin/deployment/orchestration/PortfolioOrchestrator.js +0 -273
- package/dist/bin/deployment/orchestration/SingleServiceOrchestrator.js +0 -231
- package/dist/bin/deployment/orchestration/UnifiedDeploymentOrchestrator.js +0 -662
- package/dist/bin/deployment/test-interactive-utils.js +0 -66
- package/dist/bin/portfolio/portfolio-manager.js +0 -487
- package/dist/bin/service-management/create-service.js +0 -122
- package/dist/bin/service-management/init-service.js +0 -79
- package/dist/config/customers.js +0 -623
- package/dist/config/domains.js +0 -186
- package/dist/config/index.js +0 -6
- package/dist/database/database-orchestrator.js +0 -795
- package/dist/database/index.js +0 -4
- package/dist/deployment/index.js +0 -11
- package/dist/deployment/orchestration/BaseDeploymentOrchestrator.js +0 -426
- package/dist/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
- package/dist/deployment/orchestration/PortfolioOrchestrator.js +0 -273
- package/dist/deployment/orchestration/SingleServiceOrchestrator.js +0 -231
- package/dist/deployment/orchestration/UnifiedDeploymentOrchestrator.js +0 -662
- package/dist/deployment/orchestration/index.js +0 -17
- package/dist/deployment/rollback-manager.js +0 -36
- package/dist/deployment/wrangler-deployer.js +0 -640
- package/dist/handlers/GenericRouteHandler.js +0 -532
- package/dist/migration/MigrationAdapters.js +0 -562
- package/dist/modules/ModuleManager.js +0 -668
- package/dist/modules/security.js +0 -96
- package/dist/orchestration/cross-domain-coordinator.js +0 -1083
- package/dist/orchestration/index.js +0 -5
- package/dist/orchestration/modules/DeploymentCoordinator.js +0 -368
- package/dist/orchestration/modules/DomainResolver.js +0 -198
- package/dist/orchestration/modules/StateManager.js +0 -332
- package/dist/orchestration/multi-domain-orchestrator.js +0 -724
- package/dist/routing/EnhancedRouter.js +0 -158
- package/dist/schema/SchemaManager.js +0 -778
- package/dist/service-management/ConfirmationEngine.js +0 -412
- package/dist/service-management/ErrorTracker.js +0 -299
- package/dist/service-management/GenerationEngine.js +0 -447
- package/dist/service-management/InputCollector.js +0 -619
- package/dist/service-management/ServiceCreator.js +0 -265
- package/dist/service-management/ServiceInitializer.js +0 -453
- package/dist/service-management/ServiceOrchestrator.js +0 -633
- package/dist/service-management/generators/BaseGenerator.js +0 -233
- package/dist/service-management/generators/GeneratorRegistry.js +0 -254
- package/dist/service-management/generators/cicd/CiWorkflowGenerator.js +0 -87
- package/dist/service-management/generators/cicd/DeployWorkflowGenerator.js +0 -106
- package/dist/service-management/generators/code/ServiceHandlersGenerator.js +0 -235
- package/dist/service-management/generators/code/ServiceMiddlewareGenerator.js +0 -116
- package/dist/service-management/generators/code/ServiceUtilsGenerator.js +0 -246
- package/dist/service-management/generators/code/WorkerIndexGenerator.js +0 -143
- package/dist/service-management/generators/config/DevelopmentEnvGenerator.js +0 -101
- package/dist/service-management/generators/config/DomainsConfigGenerator.js +0 -175
- package/dist/service-management/generators/config/EnvExampleGenerator.js +0 -178
- package/dist/service-management/generators/config/ProductionEnvGenerator.js +0 -97
- package/dist/service-management/generators/config/StagingEnvGenerator.js +0 -97
- package/dist/service-management/generators/config/WranglerTomlGenerator.js +0 -238
- package/dist/service-management/generators/core/PackageJsonGenerator.js +0 -243
- package/dist/service-management/generators/core/SiteConfigGenerator.js +0 -115
- package/dist/service-management/generators/documentation/ApiDocsGenerator.js +0 -331
- package/dist/service-management/generators/documentation/ConfigurationDocsGenerator.js +0 -294
- package/dist/service-management/generators/documentation/DeploymentDocsGenerator.js +0 -244
- package/dist/service-management/generators/documentation/ReadmeGenerator.js +0 -196
- package/dist/service-management/generators/schemas/ServiceSchemaGenerator.js +0 -190
- package/dist/service-management/generators/scripts/DeployScriptGenerator.js +0 -123
- package/dist/service-management/generators/scripts/HealthCheckScriptGenerator.js +0 -101
- package/dist/service-management/generators/scripts/SetupScriptGenerator.js +0 -88
- package/dist/service-management/generators/service-types/StaticSiteGenerator.js +0 -342
- package/dist/service-management/generators/testing/EslintConfigGenerator.js +0 -85
- package/dist/service-management/generators/testing/IntegrationTestsGenerator.js +0 -237
- package/dist/service-management/generators/testing/JestConfigGenerator.js +0 -72
- package/dist/service-management/generators/testing/UnitTestsGenerator.js +0 -277
- package/dist/service-management/generators/tooling/DockerComposeGenerator.js +0 -71
- package/dist/service-management/generators/tooling/GitignoreGenerator.js +0 -143
- package/dist/service-management/generators/utils/FileWriter.js +0 -179
- package/dist/service-management/generators/utils/PathResolver.js +0 -157
- package/dist/service-management/generators/utils/ServiceManifestGenerator.js +0 -111
- package/dist/service-management/generators/utils/TemplateEngine.js +0 -185
- package/dist/service-management/generators/utils/index.js +0 -18
- package/dist/service-management/handlers/ConfirmationHandler.js +0 -71
- package/dist/service-management/handlers/GenerationHandler.js +0 -80
- package/dist/service-management/handlers/InputHandler.js +0 -59
- package/dist/service-management/handlers/ValidationHandler.js +0 -203
- package/dist/service-management/index.js +0 -14
- package/dist/service-management/routing/DomainRouteMapper.js +0 -311
- package/dist/service-management/routing/RouteGenerator.js +0 -266
- package/dist/service-management/routing/WranglerRoutesBuilder.js +0 -273
- package/dist/service-management/routing/index.js +0 -14
- package/dist/service-management/services/DirectoryStructureService.js +0 -56
- package/dist/service-management/services/GenerationCoordinator.js +0 -208
- package/dist/service-management/services/GeneratorRegistry.js +0 -174
- package/dist/services/GenericDataService.js +0 -501
- package/dist/ui-structures/concepts/second-order-acquisition-strategy.md +0 -286
- package/dist/ui-structures/concepts/service-lifecycle-management.md +0 -150
- package/dist/ui-structures/concepts/service-manifest-guide.md +0 -309
- package/dist/ui-structures/concepts/three-tier-categorization-strategy.md +0 -231
- package/dist/ui-structures/creation/automated-generation-ui.json +0 -246
- package/dist/ui-structures/creation/core-inputs-ui.json +0 -217
- package/dist/ui-structures/creation/smart-confirmable-ui.json +0 -451
- package/dist/ui-structures/reference/absolutely-required-inputs.json +0 -315
- package/dist/ui-structures/reference/service-manifest-template.json +0 -342
- package/dist/version/VersionDetector.js +0 -723
- package/dist/worker/index.js +0 -4
- package/dist/worker/integration.js +0 -351
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import chalk from 'chalk';
|
|
12
12
|
import { askUser, askPassword, askChoice, closePrompts } from '../utils/interactive-prompts.js';
|
|
13
13
|
import { ApiTokenManager } from '../security/api-token-manager.js';
|
|
14
|
-
import { CloudflareAPI } from
|
|
14
|
+
import { CloudflareAPI } from '../../../utils/cloudflare/api.js';
|
|
15
15
|
export class DeploymentCredentialCollector {
|
|
16
16
|
constructor(options = {}) {
|
|
17
17
|
this.servicePath = options.servicePath || '.';
|
|
@@ -32,13 +32,16 @@ export class DeploymentCredentialCollector {
|
|
|
32
32
|
const startCredentials = {
|
|
33
33
|
token: options.token || process.env.CLOUDFLARE_API_TOKEN || null,
|
|
34
34
|
accountId: options.accountId || process.env.CLOUDFLARE_ACCOUNT_ID || null,
|
|
35
|
-
zoneId: options.zoneId || process.env.CLOUDFLARE_ZONE_ID || null
|
|
35
|
+
zoneId: options.zoneId || process.env.CLOUDFLARE_ZONE_ID || null,
|
|
36
|
+
zoneName: null // Will be populated when fetching zone
|
|
36
37
|
};
|
|
37
38
|
|
|
38
39
|
// All credentials provided - quick path
|
|
39
40
|
if (startCredentials.token && startCredentials.accountId && startCredentials.zoneId) {
|
|
40
41
|
if (!this.quiet) {
|
|
41
|
-
|
|
42
|
+
const tokenSource = options.token ? '--token flag' : 'environment variable';
|
|
43
|
+
console.log(chalk.blue(`\nℹ️ Using credentials from: ${tokenSource}`));
|
|
44
|
+
console.log(chalk.green('✅ All credentials provided via flags or environment variables'));
|
|
42
45
|
}
|
|
43
46
|
return startCredentials;
|
|
44
47
|
}
|
|
@@ -65,6 +68,7 @@ export class DeploymentCredentialCollector {
|
|
|
65
68
|
}
|
|
66
69
|
let accountId = startCredentials.accountId;
|
|
67
70
|
let zoneId = startCredentials.zoneId;
|
|
71
|
+
let zoneName = startCredentials.zoneName;
|
|
68
72
|
try {
|
|
69
73
|
const cloudflareAPI = new CloudflareAPI(token);
|
|
70
74
|
|
|
@@ -86,7 +90,9 @@ export class DeploymentCredentialCollector {
|
|
|
86
90
|
|
|
87
91
|
// If zone ID not provided, fetch from Cloudflare
|
|
88
92
|
if (!zoneId) {
|
|
89
|
-
|
|
93
|
+
const zoneInfo = await this.fetchZoneId(cloudflareAPI);
|
|
94
|
+
zoneId = zoneInfo.id;
|
|
95
|
+
zoneName = zoneInfo.name;
|
|
90
96
|
}
|
|
91
97
|
} catch (error) {
|
|
92
98
|
console.error(chalk.red(`\n❌ Credential validation failed:`));
|
|
@@ -96,10 +102,21 @@ export class DeploymentCredentialCollector {
|
|
|
96
102
|
if (!this.quiet) {
|
|
97
103
|
console.log(chalk.green('\n✅ All credentials collected and validated\n'));
|
|
98
104
|
}
|
|
105
|
+
|
|
106
|
+
// Return structured Cloudflare settings object
|
|
107
|
+
// This encapsulates all zone-specific configuration for multi-domain deployments
|
|
99
108
|
return {
|
|
100
109
|
token,
|
|
101
110
|
accountId,
|
|
102
|
-
zoneId
|
|
111
|
+
zoneId,
|
|
112
|
+
zoneName,
|
|
113
|
+
// Convenience: cloudflareSettings object for passing to orchestrators
|
|
114
|
+
cloudflareSettings: {
|
|
115
|
+
token,
|
|
116
|
+
accountId,
|
|
117
|
+
zoneId,
|
|
118
|
+
zoneName
|
|
119
|
+
}
|
|
103
120
|
};
|
|
104
121
|
}
|
|
105
122
|
|
|
@@ -197,7 +214,10 @@ export class DeploymentCredentialCollector {
|
|
|
197
214
|
if (!this.quiet) {
|
|
198
215
|
console.log(chalk.green(`✅ Found domain: ${zones[0].name}\n`));
|
|
199
216
|
}
|
|
200
|
-
return
|
|
217
|
+
return {
|
|
218
|
+
id: zones[0].id,
|
|
219
|
+
name: zones[0].name
|
|
220
|
+
};
|
|
201
221
|
}
|
|
202
222
|
|
|
203
223
|
// Multiple zones - let user choose
|
|
@@ -210,7 +230,10 @@ export class DeploymentCredentialCollector {
|
|
|
210
230
|
if (!this.quiet) {
|
|
211
231
|
console.log(chalk.green(`✅ Selected: ${selectedZone.name}\n`));
|
|
212
232
|
}
|
|
213
|
-
return
|
|
233
|
+
return {
|
|
234
|
+
id: selectedZone.id,
|
|
235
|
+
name: selectedZone.name
|
|
236
|
+
};
|
|
214
237
|
} catch (error) {
|
|
215
238
|
console.error(chalk.red('❌ Failed to fetch domains:'));
|
|
216
239
|
console.error(chalk.yellow(error.message));
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
export { DeploymentValidator } from './validator.js';
|
|
7
|
-
export { MultiDomainOrchestrator } from '../../
|
|
8
|
-
export { CrossDomainCoordinator } from '../../
|
|
7
|
+
export { MultiDomainOrchestrator } from '../../orchestration/multi-domain-orchestrator.js';
|
|
8
|
+
export { CrossDomainCoordinator } from '../../orchestration/cross-domain-coordinator.js';
|
|
9
9
|
export { DeploymentAuditor } from './auditor.js';
|
|
10
10
|
export { RollbackManager } from './rollback-manager.js';
|
|
@@ -1,523 +1,7 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
|
-
* Rollback Manager Module
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* Extracted from bulletproof-deploy.js with enhancements
|
|
2
|
+
* Rollback Manager - Deployment Module Re-export
|
|
3
|
+
*
|
|
4
|
+
* Re-exports the RollbackManager for deployment operations
|
|
8
5
|
*/
|
|
9
|
-
import { access, readFile, writeFile, mkdir, copyFile } from 'fs/promises';
|
|
10
|
-
import { promisify } from 'util';
|
|
11
|
-
import { exec } from 'child_process';
|
|
12
|
-
const execAsync = promisify(exec);
|
|
13
|
-
import { join, dirname } from 'path';
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Advanced Rollback Manager
|
|
17
|
-
* Provides comprehensive rollback capabilities for deployment failures
|
|
18
|
-
*/
|
|
19
|
-
export class RollbackManager {
|
|
20
|
-
constructor(options = {}) {
|
|
21
|
-
this.deploymentId = options.deploymentId || this.generateRollbackId();
|
|
22
|
-
this.environment = options.environment || 'production';
|
|
23
|
-
this.dryRun = options.dryRun || false;
|
|
24
|
-
this.retryAttempts = options.retryAttempts || 3;
|
|
25
|
-
this.retryDelay = options.retryDelay || 2000;
|
|
26
|
-
|
|
27
|
-
// Rollback state tracking
|
|
28
|
-
this.rollbackPlan = {
|
|
29
|
-
id: this.deploymentId,
|
|
30
|
-
created: new Date(),
|
|
31
|
-
actions: [],
|
|
32
|
-
backups: new Map(),
|
|
33
|
-
status: 'initialized',
|
|
34
|
-
executedActions: [],
|
|
35
|
-
failedActions: [],
|
|
36
|
-
totalActions: 0
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
// Backup directories
|
|
40
|
-
this.backupPaths = {
|
|
41
|
-
root: 'backups',
|
|
42
|
-
deployment: join('backups', 'deployments', this.deploymentId),
|
|
43
|
-
configs: join('backups', 'configs', this.deploymentId),
|
|
44
|
-
secrets: join('backups', 'secrets', this.deploymentId),
|
|
45
|
-
database: join('backups', 'database', this.deploymentId)
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
// Note: Async initialization required - call initialize() after construction
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Initialize the rollback manager asynchronously
|
|
53
|
-
*/
|
|
54
|
-
async initialize() {
|
|
55
|
-
await this.initializeRollbackSystem();
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Generate unique rollback identifier
|
|
60
|
-
* @returns {string} Rollback ID
|
|
61
|
-
*/
|
|
62
|
-
generateRollbackId() {
|
|
63
|
-
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
64
|
-
const random = Math.random().toString(36).substring(2, 8);
|
|
65
|
-
return `rollback-${timestamp}-${random}`;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Initialize rollback system and create backup directories
|
|
70
|
-
*/
|
|
71
|
-
async initializeRollbackSystem() {
|
|
72
|
-
console.log('🔄 Rollback System v1.0');
|
|
73
|
-
console.log('========================');
|
|
74
|
-
console.log(`🆔 Rollback ID: ${this.rollbackPlan.id}`);
|
|
75
|
-
console.log(`🌍 Environment: ${this.environment}`);
|
|
76
|
-
console.log(`🔍 Mode: ${this.dryRun ? 'DRY RUN' : 'LIVE ROLLBACK'}`);
|
|
77
|
-
console.log('');
|
|
78
|
-
|
|
79
|
-
// Create backup directories
|
|
80
|
-
for (const path of Object.values(this.backupPaths)) {
|
|
81
|
-
try {
|
|
82
|
-
await access(path);
|
|
83
|
-
} catch {
|
|
84
|
-
await mkdir(path, {
|
|
85
|
-
recursive: true
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
this.logRollbackEvent('SYSTEM_INITIALIZED', {
|
|
90
|
-
backupPaths: this.backupPaths,
|
|
91
|
-
environment: this.environment
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Add rollback action to the plan
|
|
97
|
-
* @param {Object} action - Rollback action configuration
|
|
98
|
-
*/
|
|
99
|
-
addRollbackAction(action) {
|
|
100
|
-
const rollbackAction = {
|
|
101
|
-
id: `action-${this.rollbackPlan.actions.length + 1}`,
|
|
102
|
-
timestamp: new Date(),
|
|
103
|
-
...action
|
|
104
|
-
};
|
|
105
|
-
this.rollbackPlan.actions.push(rollbackAction);
|
|
106
|
-
this.rollbackPlan.totalActions++;
|
|
107
|
-
this.logRollbackEvent('ACTION_ADDED', rollbackAction);
|
|
108
|
-
console.log(`📝 Rollback action added: ${action.type} - ${action.description || 'No description'}`);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Create backup of current state before deployment
|
|
113
|
-
* @param {Object} options - Backup options
|
|
114
|
-
* @returns {Promise<Object>} Backup manifest
|
|
115
|
-
*/
|
|
116
|
-
async createStateBackup(options = {}) {
|
|
117
|
-
console.log('💾 Creating deployment state backup...');
|
|
118
|
-
const backupManifest = {
|
|
119
|
-
id: this.deploymentId,
|
|
120
|
-
timestamp: new Date(),
|
|
121
|
-
environment: this.environment,
|
|
122
|
-
files: [],
|
|
123
|
-
cloudflareState: {},
|
|
124
|
-
databaseState: {}
|
|
125
|
-
};
|
|
126
|
-
try {
|
|
127
|
-
// Backup configuration files
|
|
128
|
-
await this.backupConfigurationFiles(backupManifest);
|
|
129
|
-
|
|
130
|
-
// Backup Cloudflare state
|
|
131
|
-
if (options.includeCloudflare !== false) {
|
|
132
|
-
await this.backupCloudflareState(backupManifest);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Backup database state
|
|
136
|
-
if (options.includeDatabase !== false) {
|
|
137
|
-
await this.backupDatabaseState(backupManifest);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Save backup manifest
|
|
141
|
-
const manifestPath = join(this.backupPaths.deployment, 'backup-manifest.json');
|
|
142
|
-
await writeFile(manifestPath, JSON.stringify(backupManifest, null, 2));
|
|
143
|
-
this.rollbackPlan.backups.set('state', backupManifest);
|
|
144
|
-
this.logRollbackEvent('BACKUP_CREATED', {
|
|
145
|
-
files: backupManifest.files.length,
|
|
146
|
-
manifestPath
|
|
147
|
-
});
|
|
148
|
-
console.log(`✅ State backup created: ${backupManifest.files.length} files backed up`);
|
|
149
|
-
return backupManifest;
|
|
150
|
-
} catch (error) {
|
|
151
|
-
this.logRollbackEvent('BACKUP_FAILED', {
|
|
152
|
-
error: error.message
|
|
153
|
-
});
|
|
154
|
-
throw new Error(`State backup failed: ${error.message}`);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Backup configuration files
|
|
160
|
-
* @param {Object} manifest - Backup manifest to update
|
|
161
|
-
*/
|
|
162
|
-
async backupConfigurationFiles(manifest) {
|
|
163
|
-
const configFiles = ['package.json', 'wrangler.toml', '.env', 'src/config/domains.js'];
|
|
164
|
-
for (const file of configFiles) {
|
|
165
|
-
try {
|
|
166
|
-
await access(file);
|
|
167
|
-
const backupPath = join(this.backupPaths.configs, file.replace(/[/\\]/g, '_'));
|
|
168
|
-
const backupDir = dirname(backupPath);
|
|
169
|
-
try {
|
|
170
|
-
await access(backupDir);
|
|
171
|
-
} catch {
|
|
172
|
-
await mkdir(backupDir, {
|
|
173
|
-
recursive: true
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
await copyFile(file, backupPath);
|
|
177
|
-
manifest.files.push({
|
|
178
|
-
original: file,
|
|
179
|
-
backup: backupPath,
|
|
180
|
-
timestamp: new Date()
|
|
181
|
-
});
|
|
182
|
-
console.log(` 📄 Backed up: ${file}`);
|
|
183
|
-
|
|
184
|
-
// Add restoration action
|
|
185
|
-
this.addRollbackAction({
|
|
186
|
-
type: 'restore-file',
|
|
187
|
-
description: `Restore ${file}`,
|
|
188
|
-
original: file,
|
|
189
|
-
backup: backupPath,
|
|
190
|
-
command: `copy "${backupPath}" "${file}"`,
|
|
191
|
-
priority: 1
|
|
192
|
-
});
|
|
193
|
-
} catch (error) {
|
|
194
|
-
console.warn(` ⚠️ Failed to backup ${file}: ${error.message}`);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Backup Cloudflare worker and secrets state
|
|
201
|
-
* @param {Object} manifest - Backup manifest to update
|
|
202
|
-
*/
|
|
203
|
-
async backupCloudflareState(manifest) {
|
|
204
|
-
try {
|
|
205
|
-
console.log(' ☁️ Backing up Cloudflare state...');
|
|
206
|
-
|
|
207
|
-
// Get current worker list
|
|
208
|
-
const workerList = await this.executeCommand('npx wrangler list', {
|
|
209
|
-
timeout: 30000
|
|
210
|
-
});
|
|
211
|
-
manifest.cloudflareState.workers = workerList;
|
|
212
|
-
|
|
213
|
-
// Get current secrets (we can't read values, but we can list keys)
|
|
214
|
-
try {
|
|
215
|
-
const secretsList = await this.executeCommand('npx wrangler secret list', {
|
|
216
|
-
timeout: 30000
|
|
217
|
-
});
|
|
218
|
-
manifest.cloudflareState.secrets = secretsList;
|
|
219
|
-
} catch (error) {
|
|
220
|
-
console.log(` ⚠️ Could not backup secrets list: ${error.message}`);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Save Cloudflare state
|
|
224
|
-
const cloudflareBackupPath = join(this.backupPaths.deployment, 'cloudflare-state.json');
|
|
225
|
-
await writeFile(cloudflareBackupPath, JSON.stringify(manifest.cloudflareState, null, 2));
|
|
226
|
-
console.log(' ✅ Cloudflare state backed up');
|
|
227
|
-
} catch (error) {
|
|
228
|
-
console.log(` ⚠️ Cloudflare backup failed: ${error.message}`);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Backup database state
|
|
234
|
-
* @param {Object} manifest - Backup manifest to update
|
|
235
|
-
*/
|
|
236
|
-
async backupDatabaseState(manifest) {
|
|
237
|
-
try {
|
|
238
|
-
console.log(' 🗄️ Backing up database state...');
|
|
239
|
-
|
|
240
|
-
// Get D1 database list
|
|
241
|
-
const dbList = await this.executeCommand('npx wrangler d1 list', {
|
|
242
|
-
timeout: 30000
|
|
243
|
-
});
|
|
244
|
-
manifest.databaseState.databases = dbList;
|
|
245
|
-
|
|
246
|
-
// Save database state
|
|
247
|
-
const dbBackupPath = join(this.backupPaths.database, 'database-state.json');
|
|
248
|
-
await writeFile(dbBackupPath, JSON.stringify(manifest.databaseState, null, 2));
|
|
249
|
-
console.log(' ✅ Database state backed up');
|
|
250
|
-
} catch (error) {
|
|
251
|
-
console.log(` ⚠️ Database backup failed: ${error.message}`);
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Execute rollback plan
|
|
257
|
-
* @returns {Promise<Object>} Rollback results
|
|
258
|
-
*/
|
|
259
|
-
async executeRollback() {
|
|
260
|
-
console.log('\n🔄 EXECUTING ROLLBACK PLAN');
|
|
261
|
-
console.log('===========================');
|
|
262
|
-
console.log(`📊 Total Actions: ${this.rollbackPlan.totalActions}`);
|
|
263
|
-
console.log(`🆔 Rollback ID: ${this.rollbackPlan.id}`);
|
|
264
|
-
console.log('');
|
|
265
|
-
this.rollbackPlan.status = 'executing';
|
|
266
|
-
this.rollbackPlan.startTime = new Date();
|
|
267
|
-
const results = {
|
|
268
|
-
rollbackId: this.rollbackPlan.id,
|
|
269
|
-
successful: [],
|
|
270
|
-
failed: [],
|
|
271
|
-
skipped: [],
|
|
272
|
-
totalActions: this.rollbackPlan.totalActions
|
|
273
|
-
};
|
|
274
|
-
try {
|
|
275
|
-
// Sort actions by priority (higher priority first)
|
|
276
|
-
const sortedActions = this.rollbackPlan.actions.sort((a, b) => (b.priority || 0) - (a.priority || 0));
|
|
277
|
-
for (const action of sortedActions) {
|
|
278
|
-
const actionResult = await this.executeRollbackAction(action);
|
|
279
|
-
if (actionResult.success) {
|
|
280
|
-
results.successful.push(actionResult);
|
|
281
|
-
this.rollbackPlan.executedActions.push(action);
|
|
282
|
-
} else {
|
|
283
|
-
results.failed.push(actionResult);
|
|
284
|
-
this.rollbackPlan.failedActions.push(action);
|
|
285
|
-
|
|
286
|
-
// Stop on critical failures unless forced to continue
|
|
287
|
-
if (action.critical !== false && !action.continueOnFailure) {
|
|
288
|
-
console.log(`❌ Critical rollback action failed: ${action.type}`);
|
|
289
|
-
break;
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
this.rollbackPlan.status = results.failed.length === 0 ? 'completed' : 'partial';
|
|
294
|
-
this.rollbackPlan.endTime = new Date();
|
|
295
|
-
const duration = (this.rollbackPlan.endTime - this.rollbackPlan.startTime) / 1000;
|
|
296
|
-
console.log('\n📊 ROLLBACK SUMMARY');
|
|
297
|
-
console.log('===================');
|
|
298
|
-
console.log(`✅ Successful: ${results.successful.length}`);
|
|
299
|
-
console.log(`❌ Failed: ${results.failed.length}`);
|
|
300
|
-
console.log(`⏸️ Skipped: ${results.skipped.length}`);
|
|
301
|
-
console.log(`⏱️ Duration: ${duration.toFixed(1)}s`);
|
|
302
|
-
console.log(`🏁 Status: ${this.rollbackPlan.status.toUpperCase()}`);
|
|
303
|
-
if (results.failed.length > 0) {
|
|
304
|
-
console.log('\n❌ Failed Actions:');
|
|
305
|
-
results.failed.forEach(failure => {
|
|
306
|
-
console.log(` - ${failure.action.type}: ${failure.error}`);
|
|
307
|
-
});
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// Save rollback report
|
|
311
|
-
await this.saveRollbackReport(results);
|
|
312
|
-
this.logRollbackEvent('ROLLBACK_COMPLETED', {
|
|
313
|
-
status: this.rollbackPlan.status,
|
|
314
|
-
successful: results.successful.length,
|
|
315
|
-
failed: results.failed.length,
|
|
316
|
-
duration
|
|
317
|
-
});
|
|
318
|
-
return results;
|
|
319
|
-
} catch (error) {
|
|
320
|
-
this.rollbackPlan.status = 'failed';
|
|
321
|
-
this.rollbackPlan.endTime = new Date();
|
|
322
|
-
this.logRollbackEvent('ROLLBACK_FAILED', {
|
|
323
|
-
error: error.message
|
|
324
|
-
});
|
|
325
|
-
throw new Error(`Rollback execution failed: ${error.message}`);
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
/**
|
|
330
|
-
* Execute individual rollback action
|
|
331
|
-
* @param {Object} action - Action to execute
|
|
332
|
-
* @returns {Promise<Object>} Action result
|
|
333
|
-
*/
|
|
334
|
-
async executeRollbackAction(action) {
|
|
335
|
-
console.log(`🔄 Rolling back: ${action.type} - ${action.description || action.id}`);
|
|
336
|
-
const result = {
|
|
337
|
-
actionId: action.id,
|
|
338
|
-
action: action,
|
|
339
|
-
success: false,
|
|
340
|
-
error: null,
|
|
341
|
-
timestamp: new Date()
|
|
342
|
-
};
|
|
343
|
-
try {
|
|
344
|
-
if (this.dryRun) {
|
|
345
|
-
console.log(` 🔍 DRY RUN: Would execute ${action.type}`);
|
|
346
|
-
result.success = true;
|
|
347
|
-
result.dryRun = true;
|
|
348
|
-
return result;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
// Execute based on action type
|
|
352
|
-
switch (action.type) {
|
|
353
|
-
case 'restore-file':
|
|
354
|
-
await this.restoreFile(action);
|
|
355
|
-
break;
|
|
356
|
-
case 'delete-secret':
|
|
357
|
-
await this.deleteSecret(action);
|
|
358
|
-
break;
|
|
359
|
-
case 'delete-database':
|
|
360
|
-
await this.deleteDatabase(action);
|
|
361
|
-
break;
|
|
362
|
-
case 'delete-worker':
|
|
363
|
-
await this.deleteWorker(action);
|
|
364
|
-
break;
|
|
365
|
-
case 'custom-command':
|
|
366
|
-
await this.executeCustomCommand(action);
|
|
367
|
-
break;
|
|
368
|
-
default:
|
|
369
|
-
if (action.command) {
|
|
370
|
-
await this.executeCommand(action.command, {
|
|
371
|
-
timeout: action.timeout || 30000
|
|
372
|
-
});
|
|
373
|
-
} else {
|
|
374
|
-
throw new Error(`Unknown rollback action type: ${action.type}`);
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
result.success = true;
|
|
378
|
-
console.log(` ✅ Rollback completed: ${action.type}`);
|
|
379
|
-
} catch (error) {
|
|
380
|
-
result.error = error.message;
|
|
381
|
-
console.log(` ❌ Rollback failed: ${action.type} - ${error.message}`);
|
|
382
|
-
}
|
|
383
|
-
return result;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
// Individual rollback action implementations
|
|
387
|
-
|
|
388
|
-
async restoreFile(action) {
|
|
389
|
-
try {
|
|
390
|
-
await access(action.backup);
|
|
391
|
-
} catch {
|
|
392
|
-
throw new Error(`Backup file not found: ${action.backup}`);
|
|
393
|
-
}
|
|
394
|
-
await copyFile(action.backup, action.original);
|
|
395
|
-
console.log(` 📄 Restored ${action.original}`);
|
|
396
|
-
}
|
|
397
|
-
async deleteSecret(action) {
|
|
398
|
-
const command = action.command || `npx wrangler secret delete ${action.key} --env ${this.environment}`;
|
|
399
|
-
await this.executeCommand(command, {
|
|
400
|
-
timeout: 30000
|
|
401
|
-
});
|
|
402
|
-
console.log(` 🔐 Deleted secret: ${action.key}`);
|
|
403
|
-
}
|
|
404
|
-
async deleteDatabase(action) {
|
|
405
|
-
const command = action.command || `npx wrangler d1 delete ${action.name} --skip-confirmation`;
|
|
406
|
-
await this.executeCommand(command, {
|
|
407
|
-
timeout: 60000
|
|
408
|
-
});
|
|
409
|
-
console.log(` 🗄️ Deleted database: ${action.name}`);
|
|
410
|
-
}
|
|
411
|
-
async deleteWorker(action) {
|
|
412
|
-
const command = action.command || `npx wrangler delete ${action.name} --env ${this.environment}`;
|
|
413
|
-
await this.executeCommand(command, {
|
|
414
|
-
timeout: 60000
|
|
415
|
-
});
|
|
416
|
-
console.log(` ⚡ Deleted worker: ${action.name}`);
|
|
417
|
-
}
|
|
418
|
-
async executeCustomCommand(action) {
|
|
419
|
-
await this.executeCommand(action.command, {
|
|
420
|
-
timeout: action.timeout || 30000
|
|
421
|
-
});
|
|
422
|
-
console.log(` 🔧 Executed: ${action.description}`);
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
// Utility methods
|
|
426
|
-
|
|
427
|
-
async executeCommand(command, options = {}) {
|
|
428
|
-
const timeout = options.timeout || 30000;
|
|
429
|
-
for (let attempt = 1; attempt <= this.retryAttempts; attempt++) {
|
|
430
|
-
try {
|
|
431
|
-
const {
|
|
432
|
-
stdout
|
|
433
|
-
} = await execAsync(command, {
|
|
434
|
-
encoding: 'utf8',
|
|
435
|
-
stdio: options.stdio || 'pipe',
|
|
436
|
-
timeout
|
|
437
|
-
});
|
|
438
|
-
return stdout;
|
|
439
|
-
} catch (error) {
|
|
440
|
-
if (attempt === this.retryAttempts) {
|
|
441
|
-
throw error;
|
|
442
|
-
}
|
|
443
|
-
console.log(` ⚠️ Attempt ${attempt}/${this.retryAttempts} failed, retrying...`);
|
|
444
|
-
await new Promise(resolve => setTimeout(resolve, this.retryDelay));
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
async saveRollbackReport(results) {
|
|
449
|
-
const report = {
|
|
450
|
-
rollbackId: this.rollbackPlan.id,
|
|
451
|
-
environment: this.environment,
|
|
452
|
-
timestamp: new Date(),
|
|
453
|
-
plan: this.rollbackPlan,
|
|
454
|
-
results,
|
|
455
|
-
summary: {
|
|
456
|
-
totalActions: results.totalActions,
|
|
457
|
-
successful: results.successful.length,
|
|
458
|
-
failed: results.failed.length,
|
|
459
|
-
successRate: (results.successful.length / results.totalActions * 100).toFixed(1)
|
|
460
|
-
}
|
|
461
|
-
};
|
|
462
|
-
const reportPath = join(this.backupPaths.deployment, 'rollback-report.json');
|
|
463
|
-
await writeFile(reportPath, JSON.stringify(report, null, 2));
|
|
464
|
-
console.log(`📊 Rollback report saved: ${reportPath}`);
|
|
465
|
-
}
|
|
466
|
-
logRollbackEvent(event, details = {}) {
|
|
467
|
-
const logEntry = {
|
|
468
|
-
timestamp: new Date().toISOString(),
|
|
469
|
-
rollbackId: this.rollbackPlan.id,
|
|
470
|
-
event,
|
|
471
|
-
details
|
|
472
|
-
};
|
|
473
|
-
|
|
474
|
-
// Log to file if not in dry run mode (fire and forget)
|
|
475
|
-
if (!this.dryRun) {
|
|
476
|
-
(async () => {
|
|
477
|
-
try {
|
|
478
|
-
const logPath = join(this.backupPaths.deployment, 'rollback-log.json');
|
|
479
|
-
let logs = [];
|
|
480
|
-
try {
|
|
481
|
-
const logData = await readFile(logPath, 'utf8');
|
|
482
|
-
logs = JSON.parse(logData);
|
|
483
|
-
} catch {
|
|
484
|
-
// File doesn't exist, start with empty logs
|
|
485
|
-
}
|
|
486
|
-
logs.push(logEntry);
|
|
487
|
-
await writeFile(logPath, JSON.stringify(logs, null, 2));
|
|
488
|
-
} catch (error) {
|
|
489
|
-
console.warn(`⚠️ Failed to log rollback event: ${error.message}`);
|
|
490
|
-
}
|
|
491
|
-
})();
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
/**
|
|
496
|
-
* Get rollback plan status
|
|
497
|
-
* @returns {Object} Current rollback plan status
|
|
498
|
-
*/
|
|
499
|
-
getStatus() {
|
|
500
|
-
return {
|
|
501
|
-
id: this.rollbackPlan.id,
|
|
502
|
-
status: this.rollbackPlan.status,
|
|
503
|
-
totalActions: this.rollbackPlan.totalActions,
|
|
504
|
-
executedActions: this.rollbackPlan.executedActions.length,
|
|
505
|
-
failedActions: this.rollbackPlan.failedActions.length,
|
|
506
|
-
created: this.rollbackPlan.created,
|
|
507
|
-
lastUpdated: new Date()
|
|
508
|
-
};
|
|
509
|
-
}
|
|
510
6
|
|
|
511
|
-
|
|
512
|
-
* Clear rollback plan (use with caution)
|
|
513
|
-
*/
|
|
514
|
-
clearRollbackPlan() {
|
|
515
|
-
this.rollbackPlan.actions = [];
|
|
516
|
-
this.rollbackPlan.totalActions = 0;
|
|
517
|
-
this.rollbackPlan.executedActions = [];
|
|
518
|
-
this.rollbackPlan.failedActions = [];
|
|
519
|
-
this.logRollbackEvent('PLAN_CLEARED');
|
|
520
|
-
console.log('🧹 Rollback plan cleared');
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
export default RollbackManager;
|
|
7
|
+
export { RollbackManager } from '../../../deployment/rollback-manager.js';
|