@tamyla/clodo-framework 2.0.20 โ†’ 3.0.3

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 (54) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/bin/clodo-service.js +1 -1
  3. package/bin/database/README.md +33 -0
  4. package/bin/database/deployment-db-manager.js +527 -0
  5. package/bin/database/enterprise-db-manager.js +736 -0
  6. package/bin/database/wrangler-d1-manager.js +775 -0
  7. package/bin/shared/cloudflare/domain-discovery.js +636 -0
  8. package/bin/shared/cloudflare/domain-manager.js +952 -0
  9. package/bin/shared/cloudflare/index.js +8 -0
  10. package/bin/shared/cloudflare/ops.js +359 -0
  11. package/bin/shared/config/index.js +1 -1
  12. package/bin/shared/database/connection-manager.js +374 -0
  13. package/bin/shared/database/index.js +7 -0
  14. package/bin/shared/database/orchestrator.js +726 -0
  15. package/bin/shared/deployment/auditor.js +969 -0
  16. package/bin/shared/deployment/index.js +10 -0
  17. package/bin/shared/deployment/rollback-manager.js +570 -0
  18. package/bin/shared/deployment/validator.js +779 -0
  19. package/bin/shared/index.js +32 -0
  20. package/bin/shared/monitoring/health-checker.js +484 -0
  21. package/bin/shared/monitoring/index.js +8 -0
  22. package/bin/shared/monitoring/memory-manager.js +387 -0
  23. package/bin/shared/monitoring/production-monitor.js +391 -0
  24. package/bin/shared/production-tester/api-tester.js +82 -0
  25. package/bin/shared/production-tester/auth-tester.js +132 -0
  26. package/bin/shared/production-tester/core.js +197 -0
  27. package/bin/shared/production-tester/database-tester.js +109 -0
  28. package/bin/shared/production-tester/index.js +77 -0
  29. package/bin/shared/production-tester/load-tester.js +131 -0
  30. package/bin/shared/production-tester/performance-tester.js +103 -0
  31. package/bin/shared/security/api-token-manager.js +312 -0
  32. package/bin/shared/security/index.js +8 -0
  33. package/bin/shared/security/secret-generator.js +937 -0
  34. package/bin/shared/security/secure-token-manager.js +398 -0
  35. package/bin/shared/utils/error-recovery.js +225 -0
  36. package/bin/shared/utils/graceful-shutdown-manager.js +390 -0
  37. package/bin/shared/utils/index.js +9 -0
  38. package/bin/shared/utils/interactive-prompts.js +146 -0
  39. package/bin/shared/utils/interactive-utils.js +530 -0
  40. package/bin/shared/utils/rate-limiter.js +246 -0
  41. package/dist/database/database-orchestrator.js +34 -12
  42. package/dist/deployment/index.js +2 -2
  43. package/dist/orchestration/multi-domain-orchestrator.js +26 -10
  44. package/dist/service-management/GenerationEngine.js +76 -28
  45. package/dist/service-management/ServiceInitializer.js +5 -3
  46. package/dist/shared/cloudflare/domain-manager.js +1 -1
  47. package/dist/shared/cloudflare/ops.js +27 -12
  48. package/dist/shared/config/index.js +1 -1
  49. package/dist/shared/deployment/index.js +2 -2
  50. package/dist/shared/security/secret-generator.js +4 -2
  51. package/dist/shared/utils/error-recovery.js +1 -1
  52. package/dist/shared/utils/graceful-shutdown-manager.js +4 -3
  53. package/dist/utils/deployment/secret-generator.js +19 -6
  54. package/package.json +4 -2
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Deployment Module
3
+ * Exports all deployment-related orchestrators, validators, and managers
4
+ */
5
+
6
+ export { DeploymentValidator } from './validator.js';
7
+ export { MultiDomainOrchestrator } from '../../dist/orchestration/multi-domain-orchestrator.js';
8
+ export { CrossDomainCoordinator } from '../../dist/orchestration/cross-domain-coordinator.js';
9
+ export { DeploymentAuditor } from './auditor.js';
10
+ export { RollbackManager } from './rollback-manager.js';
@@ -0,0 +1,570 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Rollback Manager Module
5
+ * Enterprise-grade rollback system for safe deployment recovery
6
+ *
7
+ * Extracted from bulletproof-deploy.js with enhancements
8
+ */
9
+
10
+ import { access, readFile, writeFile, mkdir, copyFile } from 'fs/promises';
11
+ import { promisify } from 'util';
12
+ import { exec } from 'child_process';
13
+
14
+ const execAsync = promisify(exec);
15
+ import { join, dirname } from 'path';
16
+
17
+ /**
18
+ * Advanced Rollback Manager
19
+ * Provides comprehensive rollback capabilities for deployment failures
20
+ */
21
+ export class RollbackManager {
22
+ constructor(options = {}) {
23
+ this.deploymentId = options.deploymentId || this.generateRollbackId();
24
+ this.environment = options.environment || 'production';
25
+ this.dryRun = options.dryRun || false;
26
+ this.retryAttempts = options.retryAttempts || 3;
27
+ this.retryDelay = options.retryDelay || 2000;
28
+
29
+ // Rollback state tracking
30
+ this.rollbackPlan = {
31
+ id: this.deploymentId,
32
+ created: new Date(),
33
+ actions: [],
34
+ backups: new Map(),
35
+ status: 'initialized',
36
+ executedActions: [],
37
+ failedActions: [],
38
+ totalActions: 0
39
+ };
40
+
41
+ // Backup directories
42
+ this.backupPaths = {
43
+ root: 'backups',
44
+ deployment: join('backups', 'deployments', this.deploymentId),
45
+ configs: join('backups', 'configs', this.deploymentId),
46
+ secrets: join('backups', 'secrets', this.deploymentId),
47
+ database: join('backups', 'database', this.deploymentId)
48
+ };
49
+
50
+ // Note: Async initialization required - call initialize() after construction
51
+ }
52
+
53
+ /**
54
+ * Initialize the rollback manager asynchronously
55
+ */
56
+ async initialize() {
57
+ await this.initializeRollbackSystem();
58
+ }
59
+
60
+ /**
61
+ * Generate unique rollback identifier
62
+ * @returns {string} Rollback ID
63
+ */
64
+ generateRollbackId() {
65
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
66
+ const random = Math.random().toString(36).substring(2, 8);
67
+ return `rollback-${timestamp}-${random}`;
68
+ }
69
+
70
+ /**
71
+ * Initialize rollback system and create backup directories
72
+ */
73
+ async initializeRollbackSystem() {
74
+ console.log('๐Ÿ”„ Rollback System v1.0');
75
+ console.log('========================');
76
+ console.log(`๐Ÿ†” Rollback ID: ${this.rollbackPlan.id}`);
77
+ console.log(`๐ŸŒ Environment: ${this.environment}`);
78
+ console.log(`๐Ÿ” Mode: ${this.dryRun ? 'DRY RUN' : 'LIVE ROLLBACK'}`);
79
+ console.log('');
80
+
81
+ // Create backup directories
82
+ for (const path of Object.values(this.backupPaths)) {
83
+ try {
84
+ await access(path);
85
+ } catch {
86
+ await mkdir(path, { recursive: true });
87
+ }
88
+ }
89
+
90
+ this.logRollbackEvent('SYSTEM_INITIALIZED', {
91
+ backupPaths: this.backupPaths,
92
+ environment: this.environment
93
+ });
94
+ }
95
+
96
+ /**
97
+ * Add rollback action to the plan
98
+ * @param {Object} action - Rollback action configuration
99
+ */
100
+ addRollbackAction(action) {
101
+ const rollbackAction = {
102
+ id: `action-${this.rollbackPlan.actions.length + 1}`,
103
+ timestamp: new Date(),
104
+ ...action
105
+ };
106
+
107
+ this.rollbackPlan.actions.push(rollbackAction);
108
+ this.rollbackPlan.totalActions++;
109
+
110
+ this.logRollbackEvent('ACTION_ADDED', rollbackAction);
111
+
112
+ console.log(`๐Ÿ“ Rollback action added: ${action.type} - ${action.description || 'No description'}`);
113
+ }
114
+
115
+ /**
116
+ * Create backup of current state before deployment
117
+ * @param {Object} options - Backup options
118
+ * @returns {Promise<Object>} Backup manifest
119
+ */
120
+ async createStateBackup(options = {}) {
121
+ console.log('๐Ÿ’พ Creating deployment state backup...');
122
+
123
+ const backupManifest = {
124
+ id: this.deploymentId,
125
+ timestamp: new Date(),
126
+ environment: this.environment,
127
+ files: [],
128
+ cloudflareState: {},
129
+ databaseState: {}
130
+ };
131
+
132
+ try {
133
+ // Backup configuration files
134
+ await this.backupConfigurationFiles(backupManifest);
135
+
136
+ // Backup Cloudflare state
137
+ if (options.includeCloudflare !== false) {
138
+ await this.backupCloudflareState(backupManifest);
139
+ }
140
+
141
+ // Backup database state
142
+ if (options.includeDatabase !== false) {
143
+ await this.backupDatabaseState(backupManifest);
144
+ }
145
+
146
+ // Save backup manifest
147
+ const manifestPath = join(this.backupPaths.deployment, 'backup-manifest.json');
148
+ await writeFile(manifestPath, JSON.stringify(backupManifest, null, 2));
149
+
150
+ this.rollbackPlan.backups.set('state', backupManifest);
151
+ this.logRollbackEvent('BACKUP_CREATED', {
152
+ files: backupManifest.files.length,
153
+ manifestPath
154
+ });
155
+
156
+ console.log(`โœ… State backup created: ${backupManifest.files.length} files backed up`);
157
+ return backupManifest;
158
+
159
+ } catch (error) {
160
+ this.logRollbackEvent('BACKUP_FAILED', { error: error.message });
161
+ throw new Error(`State backup failed: ${error.message}`);
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Backup configuration files
167
+ * @param {Object} manifest - Backup manifest to update
168
+ */
169
+ async backupConfigurationFiles(manifest) {
170
+ const configFiles = [
171
+ 'package.json',
172
+ 'wrangler.toml',
173
+ '.env',
174
+ 'src/config/domains.js'
175
+ ];
176
+
177
+ for (const file of configFiles) {
178
+ try {
179
+ await access(file);
180
+ const backupPath = join(this.backupPaths.configs, file.replace(/[/\\]/g, '_'));
181
+ const backupDir = dirname(backupPath);
182
+
183
+ try {
184
+ await access(backupDir);
185
+ } catch {
186
+ await mkdir(backupDir, { recursive: true });
187
+ }
188
+
189
+ await copyFile(file, backupPath);
190
+
191
+ manifest.files.push({
192
+ original: file,
193
+ backup: backupPath,
194
+ timestamp: new Date()
195
+ });
196
+
197
+ console.log(` ๐Ÿ“„ Backed up: ${file}`);
198
+
199
+ // Add restoration action
200
+ this.addRollbackAction({
201
+ type: 'restore-file',
202
+ description: `Restore ${file}`,
203
+ original: file,
204
+ backup: backupPath,
205
+ command: `copy "${backupPath}" "${file}"`,
206
+ priority: 1
207
+ });
208
+ } catch (error) {
209
+ console.warn(` โš ๏ธ Failed to backup ${file}: ${error.message}`);
210
+ }
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Backup Cloudflare worker and secrets state
216
+ * @param {Object} manifest - Backup manifest to update
217
+ */
218
+ async backupCloudflareState(manifest) {
219
+ try {
220
+ console.log(' โ˜๏ธ Backing up Cloudflare state...');
221
+
222
+ // Get current worker list
223
+ const workerList = await this.executeCommand('npx wrangler list', { timeout: 30000 });
224
+ manifest.cloudflareState.workers = workerList;
225
+
226
+ // Get current secrets (we can't read values, but we can list keys)
227
+ try {
228
+ const secretsList = await this.executeCommand('npx wrangler secret list', { timeout: 30000 });
229
+ manifest.cloudflareState.secrets = secretsList;
230
+ } catch (error) {
231
+ console.log(` โš ๏ธ Could not backup secrets list: ${error.message}`);
232
+ }
233
+
234
+ // Save Cloudflare state
235
+ const cloudflareBackupPath = join(this.backupPaths.deployment, 'cloudflare-state.json');
236
+ await writeFile(cloudflareBackupPath, JSON.stringify(manifest.cloudflareState, null, 2));
237
+
238
+ console.log(' โœ… Cloudflare state backed up');
239
+
240
+ } catch (error) {
241
+ console.log(` โš ๏ธ Cloudflare backup failed: ${error.message}`);
242
+ }
243
+ }
244
+
245
+ /**
246
+ * Backup database state
247
+ * @param {Object} manifest - Backup manifest to update
248
+ */
249
+ async backupDatabaseState(manifest) {
250
+ try {
251
+ console.log(' ๐Ÿ—„๏ธ Backing up database state...');
252
+
253
+ // Get D1 database list
254
+ const dbList = await this.executeCommand('npx wrangler d1 list', { timeout: 30000 });
255
+ manifest.databaseState.databases = dbList;
256
+
257
+ // Save database state
258
+ const dbBackupPath = join(this.backupPaths.database, 'database-state.json');
259
+ await writeFile(dbBackupPath, JSON.stringify(manifest.databaseState, null, 2));
260
+
261
+ console.log(' โœ… Database state backed up');
262
+
263
+ } catch (error) {
264
+ console.log(` โš ๏ธ Database backup failed: ${error.message}`);
265
+ }
266
+ }
267
+
268
+ /**
269
+ * Execute rollback plan
270
+ * @returns {Promise<Object>} Rollback results
271
+ */
272
+ async executeRollback() {
273
+ console.log('\n๐Ÿ”„ EXECUTING ROLLBACK PLAN');
274
+ console.log('===========================');
275
+ console.log(`๐Ÿ“Š Total Actions: ${this.rollbackPlan.totalActions}`);
276
+ console.log(`๐Ÿ†” Rollback ID: ${this.rollbackPlan.id}`);
277
+ console.log('');
278
+
279
+ this.rollbackPlan.status = 'executing';
280
+ this.rollbackPlan.startTime = new Date();
281
+
282
+ const results = {
283
+ rollbackId: this.rollbackPlan.id,
284
+ successful: [],
285
+ failed: [],
286
+ skipped: [],
287
+ totalActions: this.rollbackPlan.totalActions
288
+ };
289
+
290
+ try {
291
+ // Sort actions by priority (higher priority first)
292
+ const sortedActions = this.rollbackPlan.actions.sort((a, b) =>
293
+ (b.priority || 0) - (a.priority || 0)
294
+ );
295
+
296
+ for (const action of sortedActions) {
297
+ const actionResult = await this.executeRollbackAction(action);
298
+
299
+ if (actionResult.success) {
300
+ results.successful.push(actionResult);
301
+ this.rollbackPlan.executedActions.push(action);
302
+ } else {
303
+ results.failed.push(actionResult);
304
+ this.rollbackPlan.failedActions.push(action);
305
+
306
+ // Stop on critical failures unless forced to continue
307
+ if (action.critical !== false && !action.continueOnFailure) {
308
+ console.log(`โŒ Critical rollback action failed: ${action.type}`);
309
+ break;
310
+ }
311
+ }
312
+ }
313
+
314
+ this.rollbackPlan.status = results.failed.length === 0 ? 'completed' : 'partial';
315
+ this.rollbackPlan.endTime = new Date();
316
+
317
+ const duration = (this.rollbackPlan.endTime - this.rollbackPlan.startTime) / 1000;
318
+
319
+ console.log('\n๐Ÿ“Š ROLLBACK SUMMARY');
320
+ console.log('===================');
321
+ console.log(`โœ… Successful: ${results.successful.length}`);
322
+ console.log(`โŒ Failed: ${results.failed.length}`);
323
+ console.log(`โธ๏ธ Skipped: ${results.skipped.length}`);
324
+ console.log(`โฑ๏ธ Duration: ${duration.toFixed(1)}s`);
325
+ console.log(`๐Ÿ Status: ${this.rollbackPlan.status.toUpperCase()}`);
326
+
327
+ if (results.failed.length > 0) {
328
+ console.log('\nโŒ Failed Actions:');
329
+ results.failed.forEach(failure => {
330
+ console.log(` - ${failure.action.type}: ${failure.error}`);
331
+ });
332
+ }
333
+
334
+ // Save rollback report
335
+ await this.saveRollbackReport(results);
336
+
337
+ this.logRollbackEvent('ROLLBACK_COMPLETED', {
338
+ status: this.rollbackPlan.status,
339
+ successful: results.successful.length,
340
+ failed: results.failed.length,
341
+ duration
342
+ });
343
+
344
+ return results;
345
+
346
+ } catch (error) {
347
+ this.rollbackPlan.status = 'failed';
348
+ this.rollbackPlan.endTime = new Date();
349
+
350
+ this.logRollbackEvent('ROLLBACK_FAILED', { error: error.message });
351
+ throw new Error(`Rollback execution failed: ${error.message}`);
352
+ }
353
+ }
354
+
355
+ /**
356
+ * Execute individual rollback action
357
+ * @param {Object} action - Action to execute
358
+ * @returns {Promise<Object>} Action result
359
+ */
360
+ async executeRollbackAction(action) {
361
+ console.log(`๐Ÿ”„ Rolling back: ${action.type} - ${action.description || action.id}`);
362
+
363
+ const result = {
364
+ actionId: action.id,
365
+ action: action,
366
+ success: false,
367
+ error: null,
368
+ timestamp: new Date()
369
+ };
370
+
371
+ try {
372
+ if (this.dryRun) {
373
+ console.log(` ๐Ÿ” DRY RUN: Would execute ${action.type}`);
374
+ result.success = true;
375
+ result.dryRun = true;
376
+ return result;
377
+ }
378
+
379
+ // Execute based on action type
380
+ switch (action.type) {
381
+ case 'restore-file':
382
+ await this.restoreFile(action);
383
+ break;
384
+
385
+ case 'delete-secret':
386
+ await this.deleteSecret(action);
387
+ break;
388
+
389
+ case 'delete-database':
390
+ await this.deleteDatabase(action);
391
+ break;
392
+
393
+ case 'delete-worker':
394
+ await this.deleteWorker(action);
395
+ break;
396
+
397
+ case 'custom-command':
398
+ await this.executeCustomCommand(action);
399
+ break;
400
+
401
+ default:
402
+ if (action.command) {
403
+ await this.executeCommand(action.command, { timeout: action.timeout || 30000 });
404
+ } else {
405
+ throw new Error(`Unknown rollback action type: ${action.type}`);
406
+ }
407
+ }
408
+
409
+ result.success = true;
410
+ console.log(` โœ… Rollback completed: ${action.type}`);
411
+
412
+ } catch (error) {
413
+ result.error = error.message;
414
+ console.log(` โŒ Rollback failed: ${action.type} - ${error.message}`);
415
+ }
416
+
417
+ return result;
418
+ }
419
+
420
+ // Individual rollback action implementations
421
+
422
+ async restoreFile(action) {
423
+ try {
424
+ await access(action.backup);
425
+ } catch {
426
+ throw new Error(`Backup file not found: ${action.backup}`);
427
+ }
428
+
429
+ await copyFile(action.backup, action.original);
430
+ console.log(` ๐Ÿ“„ Restored ${action.original}`);
431
+ }
432
+
433
+ async deleteSecret(action) {
434
+ const command = action.command ||
435
+ `npx wrangler secret delete ${action.key} --env ${this.environment}`;
436
+
437
+ await this.executeCommand(command, { timeout: 30000 });
438
+ console.log(` ๐Ÿ” Deleted secret: ${action.key}`);
439
+ }
440
+
441
+ async deleteDatabase(action) {
442
+ const command = action.command ||
443
+ `npx wrangler d1 delete ${action.name} --skip-confirmation`;
444
+
445
+ await this.executeCommand(command, { timeout: 60000 });
446
+ console.log(` ๐Ÿ—„๏ธ Deleted database: ${action.name}`);
447
+ }
448
+
449
+ async deleteWorker(action) {
450
+ const command = action.command ||
451
+ `npx wrangler delete ${action.name} --env ${this.environment}`;
452
+
453
+ await this.executeCommand(command, { timeout: 60000 });
454
+ console.log(` โšก Deleted worker: ${action.name}`);
455
+ }
456
+
457
+ async executeCustomCommand(action) {
458
+ await this.executeCommand(action.command, {
459
+ timeout: action.timeout || 30000
460
+ });
461
+ console.log(` ๐Ÿ”ง Executed: ${action.description}`);
462
+ }
463
+
464
+ // Utility methods
465
+
466
+ async executeCommand(command, options = {}) {
467
+ const timeout = options.timeout || 30000;
468
+
469
+ for (let attempt = 1; attempt <= this.retryAttempts; attempt++) {
470
+ try {
471
+ const { stdout } = await execAsync(command, {
472
+ encoding: 'utf8',
473
+ stdio: options.stdio || 'pipe',
474
+ timeout
475
+ });
476
+ return stdout;
477
+ } catch (error) {
478
+ if (attempt === this.retryAttempts) {
479
+ throw error;
480
+ }
481
+
482
+ console.log(` โš ๏ธ Attempt ${attempt}/${this.retryAttempts} failed, retrying...`);
483
+ await new Promise(resolve => setTimeout(resolve, this.retryDelay));
484
+ }
485
+ }
486
+ }
487
+
488
+ async saveRollbackReport(results) {
489
+ const report = {
490
+ rollbackId: this.rollbackPlan.id,
491
+ environment: this.environment,
492
+ timestamp: new Date(),
493
+ plan: this.rollbackPlan,
494
+ results,
495
+ summary: {
496
+ totalActions: results.totalActions,
497
+ successful: results.successful.length,
498
+ failed: results.failed.length,
499
+ successRate: ((results.successful.length / results.totalActions) * 100).toFixed(1)
500
+ }
501
+ };
502
+
503
+ const reportPath = join(this.backupPaths.deployment, 'rollback-report.json');
504
+ await writeFile(reportPath, JSON.stringify(report, null, 2));
505
+
506
+ console.log(`๐Ÿ“Š Rollback report saved: ${reportPath}`);
507
+ }
508
+
509
+ logRollbackEvent(event, details = {}) {
510
+ const logEntry = {
511
+ timestamp: new Date().toISOString(),
512
+ rollbackId: this.rollbackPlan.id,
513
+ event,
514
+ details
515
+ };
516
+
517
+ // Log to file if not in dry run mode (fire and forget)
518
+ if (!this.dryRun) {
519
+ (async () => {
520
+ try {
521
+ const logPath = join(this.backupPaths.deployment, 'rollback-log.json');
522
+ let logs = [];
523
+
524
+ try {
525
+ const logData = await readFile(logPath, 'utf8');
526
+ logs = JSON.parse(logData);
527
+ } catch {
528
+ // File doesn't exist, start with empty logs
529
+ }
530
+
531
+ logs.push(logEntry);
532
+ await writeFile(logPath, JSON.stringify(logs, null, 2));
533
+ } catch (error) {
534
+ console.warn(`โš ๏ธ Failed to log rollback event: ${error.message}`);
535
+ }
536
+ })();
537
+ }
538
+ }
539
+
540
+ /**
541
+ * Get rollback plan status
542
+ * @returns {Object} Current rollback plan status
543
+ */
544
+ getStatus() {
545
+ return {
546
+ id: this.rollbackPlan.id,
547
+ status: this.rollbackPlan.status,
548
+ totalActions: this.rollbackPlan.totalActions,
549
+ executedActions: this.rollbackPlan.executedActions.length,
550
+ failedActions: this.rollbackPlan.failedActions.length,
551
+ created: this.rollbackPlan.created,
552
+ lastUpdated: new Date()
553
+ };
554
+ }
555
+
556
+ /**
557
+ * Clear rollback plan (use with caution)
558
+ */
559
+ clearRollbackPlan() {
560
+ this.rollbackPlan.actions = [];
561
+ this.rollbackPlan.totalActions = 0;
562
+ this.rollbackPlan.executedActions = [];
563
+ this.rollbackPlan.failedActions = [];
564
+
565
+ this.logRollbackEvent('PLAN_CLEARED');
566
+ console.log('๐Ÿงน Rollback plan cleared');
567
+ }
568
+ }
569
+
570
+ export default RollbackManager;