@tamyla/clodo-framework 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/CHANGELOG.md +564 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1393 -0
  4. package/bin/README.md +71 -0
  5. package/bin/clodo-service.js +416 -0
  6. package/bin/security/security-cli.js +96 -0
  7. package/bin/service-management/README.md +74 -0
  8. package/bin/service-management/create-service.js +129 -0
  9. package/bin/service-management/init-service.js +102 -0
  10. package/bin/service-management/init-service.js.backup +889 -0
  11. package/bin/shared/config/customer-cli.js +293 -0
  12. package/dist/config/ConfigurationManager.js +159 -0
  13. package/dist/config/CustomerConfigCLI.js +220 -0
  14. package/dist/config/FeatureManager.js +426 -0
  15. package/dist/config/customers.js +441 -0
  16. package/dist/config/domains.js +180 -0
  17. package/dist/config/features.js +225 -0
  18. package/dist/config/index.js +6 -0
  19. package/dist/database/database-orchestrator.js +730 -0
  20. package/dist/database/index.js +4 -0
  21. package/dist/deployment/auditor.js +971 -0
  22. package/dist/deployment/index.js +10 -0
  23. package/dist/deployment/rollback-manager.js +523 -0
  24. package/dist/deployment/testers/api-tester.js +80 -0
  25. package/dist/deployment/testers/auth-tester.js +129 -0
  26. package/dist/deployment/testers/core.js +217 -0
  27. package/dist/deployment/testers/database-tester.js +105 -0
  28. package/dist/deployment/testers/index.js +74 -0
  29. package/dist/deployment/testers/load-tester.js +120 -0
  30. package/dist/deployment/testers/performance-tester.js +105 -0
  31. package/dist/deployment/validator.js +558 -0
  32. package/dist/deployment/wrangler-deployer.js +574 -0
  33. package/dist/handlers/GenericRouteHandler.js +532 -0
  34. package/dist/index.js +39 -0
  35. package/dist/migration/MigrationAdapters.js +562 -0
  36. package/dist/modules/ModuleManager.js +668 -0
  37. package/dist/modules/security.js +98 -0
  38. package/dist/orchestration/cross-domain-coordinator.js +1083 -0
  39. package/dist/orchestration/index.js +5 -0
  40. package/dist/orchestration/modules/DeploymentCoordinator.js +258 -0
  41. package/dist/orchestration/modules/DomainResolver.js +196 -0
  42. package/dist/orchestration/modules/StateManager.js +332 -0
  43. package/dist/orchestration/multi-domain-orchestrator.js +255 -0
  44. package/dist/routing/EnhancedRouter.js +158 -0
  45. package/dist/schema/SchemaManager.js +778 -0
  46. package/dist/security/ConfigurationValidator.js +490 -0
  47. package/dist/security/DeploymentManager.js +208 -0
  48. package/dist/security/SecretGenerator.js +142 -0
  49. package/dist/security/SecurityCLI.js +228 -0
  50. package/dist/security/index.js +51 -0
  51. package/dist/security/patterns/environment-rules.js +66 -0
  52. package/dist/security/patterns/insecure-patterns.js +21 -0
  53. package/dist/service-management/ConfirmationEngine.js +411 -0
  54. package/dist/service-management/ErrorTracker.js +294 -0
  55. package/dist/service-management/GenerationEngine.js +3109 -0
  56. package/dist/service-management/InputCollector.js +237 -0
  57. package/dist/service-management/ServiceCreator.js +229 -0
  58. package/dist/service-management/ServiceInitializer.js +448 -0
  59. package/dist/service-management/ServiceOrchestrator.js +638 -0
  60. package/dist/service-management/handlers/ConfigMutator.js +130 -0
  61. package/dist/service-management/handlers/ConfirmationHandler.js +71 -0
  62. package/dist/service-management/handlers/GenerationHandler.js +80 -0
  63. package/dist/service-management/handlers/InputHandler.js +59 -0
  64. package/dist/service-management/handlers/ValidationHandler.js +203 -0
  65. package/dist/service-management/index.js +7 -0
  66. package/dist/services/GenericDataService.js +488 -0
  67. package/dist/shared/cloudflare/domain-discovery.js +562 -0
  68. package/dist/shared/cloudflare/domain-manager.js +912 -0
  69. package/dist/shared/cloudflare/index.js +8 -0
  70. package/dist/shared/cloudflare/ops.js +387 -0
  71. package/dist/shared/config/cache.js +1167 -0
  72. package/dist/shared/config/command-config-manager.js +174 -0
  73. package/dist/shared/config/customer-cli.js +258 -0
  74. package/dist/shared/config/index.js +9 -0
  75. package/dist/shared/config/manager.js +289 -0
  76. package/dist/shared/database/connection-manager.js +338 -0
  77. package/dist/shared/database/index.js +7 -0
  78. package/dist/shared/database/orchestrator.js +632 -0
  79. package/dist/shared/deployment/auditor.js +971 -0
  80. package/dist/shared/deployment/index.js +10 -0
  81. package/dist/shared/deployment/rollback-manager.js +523 -0
  82. package/dist/shared/deployment/validator.js +558 -0
  83. package/dist/shared/index.js +32 -0
  84. package/dist/shared/monitoring/health-checker.js +250 -0
  85. package/dist/shared/monitoring/index.js +8 -0
  86. package/dist/shared/monitoring/memory-manager.js +382 -0
  87. package/dist/shared/monitoring/production-monitor.js +390 -0
  88. package/dist/shared/production-tester/api-tester.js +80 -0
  89. package/dist/shared/production-tester/auth-tester.js +129 -0
  90. package/dist/shared/production-tester/core.js +217 -0
  91. package/dist/shared/production-tester/database-tester.js +105 -0
  92. package/dist/shared/production-tester/index.js +74 -0
  93. package/dist/shared/production-tester/load-tester.js +120 -0
  94. package/dist/shared/production-tester/performance-tester.js +105 -0
  95. package/dist/shared/security/api-token-manager.js +296 -0
  96. package/dist/shared/security/index.js +8 -0
  97. package/dist/shared/security/secret-generator.js +918 -0
  98. package/dist/shared/security/secure-token-manager.js +379 -0
  99. package/dist/shared/utils/error-recovery.js +240 -0
  100. package/dist/shared/utils/graceful-shutdown-manager.js +380 -0
  101. package/dist/shared/utils/index.js +9 -0
  102. package/dist/shared/utils/interactive-prompts.js +134 -0
  103. package/dist/shared/utils/rate-limiter.js +249 -0
  104. package/dist/utils/ErrorHandler.js +173 -0
  105. package/dist/utils/deployment/config-cache.js +1160 -0
  106. package/dist/utils/deployment/index.js +6 -0
  107. package/dist/utils/deployment/interactive-prompts.js +97 -0
  108. package/dist/utils/deployment/secret-generator.js +896 -0
  109. package/dist/utils/dirname-helper.js +35 -0
  110. package/dist/utils/domain-config.js +159 -0
  111. package/dist/utils/error-recovery.js +240 -0
  112. package/dist/utils/esm-helper.js +52 -0
  113. package/dist/utils/framework-config.js +481 -0
  114. package/dist/utils/graceful-shutdown-manager.js +379 -0
  115. package/dist/utils/health-checker.js +114 -0
  116. package/dist/utils/index.js +36 -0
  117. package/dist/utils/prompt-handler.js +98 -0
  118. package/dist/utils/usage-tracker.js +252 -0
  119. package/dist/utils/validation.js +112 -0
  120. package/dist/version/VersionDetector.js +723 -0
  121. package/dist/worker/index.js +4 -0
  122. package/dist/worker/integration.js +332 -0
  123. package/docs/FRAMEWORK-ARCHITECTURE-OVERVIEW.md +206 -0
  124. package/docs/INTEGRATION_GUIDE.md +2045 -0
  125. package/docs/README.md +82 -0
  126. package/docs/SECURITY.md +242 -0
  127. package/docs/deployment/deployment-guide.md +540 -0
  128. package/docs/overview.md +280 -0
  129. package/package.json +176 -0
  130. package/types/index.d.ts +575 -0
@@ -0,0 +1,896 @@
1
+ /**
2
+ * Enhanced Secret Generation Module
3
+ * Enterprise-grade cryptographic secret management with multi-domain and cross-environment capabilities
4
+ *
5
+ * Enhanced from deprecated smart-deployment.js with advanced features:
6
+ * - Multi-domain secret coordination
7
+ * - Cross-environment secret management
8
+ * - Multiple output formats (env, JSON, wrangler)
9
+ * - Secret rotation planning
10
+ * - Audit logging and validation
11
+ */
12
+
13
+ import { randomBytes, createHash } from 'crypto';
14
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, statSync, appendFileSync } from 'fs';
15
+ import { join, dirname } from 'path';
16
+ import { execSync } from 'child_process';
17
+ import { fileURLToPath } from 'url';
18
+ let __filename, __dirname;
19
+
20
+ // Handle test environment where import.meta might be transformed
21
+ try {
22
+ __filename = fileURLToPath(import.meta.url);
23
+ __dirname = dirname(__filename);
24
+ } catch (error) {
25
+ // Fallback for test environment
26
+ __dirname = join(process.cwd(), 'src', 'utils', 'deployment');
27
+ __filename = join(__dirname, 'secret-generator.js');
28
+ }
29
+ const SECRET_CONFIGS = {
30
+ 'AUTH_JWT_SECRET': {
31
+ length: 64,
32
+ description: 'JWT token signing secret',
33
+ scope: 'critical'
34
+ },
35
+ 'X_SERVICE_KEY': {
36
+ length: 64,
37
+ description: 'Service authentication key',
38
+ scope: 'critical'
39
+ },
40
+ 'AUTH_SERVICE_API_KEY': {
41
+ length: 48,
42
+ description: 'Auth service API key',
43
+ scope: 'standard'
44
+ },
45
+ 'LOGGER_SERVICE_API_KEY': {
46
+ length: 48,
47
+ description: 'Logger service API key',
48
+ scope: 'standard'
49
+ },
50
+ 'CONTENT_SKIMMER_API_KEY': {
51
+ length: 48,
52
+ description: 'Content service API key',
53
+ scope: 'standard'
54
+ },
55
+ 'CROSS_DOMAIN_AUTH_KEY': {
56
+ length: 64,
57
+ description: 'Cross-domain authentication key',
58
+ scope: 'critical'
59
+ },
60
+ 'WEBHOOK_SIGNATURE_KEY': {
61
+ length: 32,
62
+ description: 'Webhook signature verification key',
63
+ scope: 'standard'
64
+ },
65
+ 'FILE_ENCRYPTION_KEY': {
66
+ length: 64,
67
+ description: 'File encryption key',
68
+ scope: 'critical'
69
+ },
70
+ 'SESSION_ENCRYPTION_KEY': {
71
+ length: 48,
72
+ description: 'Session encryption key',
73
+ scope: 'standard'
74
+ },
75
+ 'API_RATE_LIMIT_KEY': {
76
+ length: 32,
77
+ description: 'API rate limiting key',
78
+ scope: 'standard'
79
+ }
80
+ };
81
+
82
+ /**
83
+ * Enhanced Secret Manager Class
84
+ * Provides enterprise-grade secret management with multi-domain coordination
85
+ */
86
+ export class EnhancedSecretManager {
87
+ constructor(options = {}) {
88
+ this.projectRoot = options.projectRoot || join(__dirname, '..', '..');
89
+ this.dryRun = options.dryRun || false;
90
+ this.retryAttempts = options.retryAttempts || 3;
91
+ this.retryDelay = options.retryDelay || 2000;
92
+
93
+ // Multi-domain and environment tracking
94
+ this.domainSecrets = new Map();
95
+ this.environmentConfigs = new Map();
96
+ this.rotationSchedule = new Map();
97
+
98
+ // Paths for secret management
99
+ this.secretPaths = {
100
+ root: join(this.projectRoot, 'secrets'),
101
+ distribution: join(this.projectRoot, 'secrets', 'distribution'),
102
+ backups: join(this.projectRoot, 'secrets', 'backups'),
103
+ audit: join(this.projectRoot, 'logs', 'secrets-audit.log'),
104
+ templates: join(this.projectRoot, 'secrets', 'templates')
105
+ };
106
+
107
+ // Supported output formats
108
+ this.outputFormats = {
109
+ env: {
110
+ extension: '.env',
111
+ description: 'Environment variables'
112
+ },
113
+ json: {
114
+ extension: '.json',
115
+ description: 'JSON format'
116
+ },
117
+ wrangler: {
118
+ extension: '.sh',
119
+ description: 'Wrangler CLI commands'
120
+ },
121
+ powershell: {
122
+ extension: '.ps1',
123
+ description: 'PowerShell commands'
124
+ },
125
+ docker: {
126
+ extension: '.env',
127
+ description: 'Docker environment file'
128
+ },
129
+ kubernetes: {
130
+ extension: '.yaml',
131
+ description: 'Kubernetes secrets'
132
+ }
133
+ };
134
+ this.initializeSecretManager();
135
+ }
136
+
137
+ /**
138
+ * Initialize enhanced secret manager
139
+ */
140
+ initializeSecretManager() {
141
+ console.log('🔐 Enhanced Secret Manager v2.0');
142
+ console.log('===============================');
143
+ console.log(`📁 Secret Root: ${this.secretPaths.root}`);
144
+ console.log(`🔍 Mode: ${this.dryRun ? 'DRY RUN' : 'LIVE OPERATIONS'}`);
145
+ console.log(`📊 Formats: ${Object.keys(this.outputFormats).join(', ')}`);
146
+ console.log('');
147
+
148
+ // Create directories
149
+ Object.values(this.secretPaths).forEach(path => {
150
+ if (!path.endsWith('.log')) {
151
+ this.ensureDirectory(path);
152
+ }
153
+ });
154
+ this.logSecretEvent('MANAGER_INITIALIZED', 'SYSTEM', {
155
+ formats: Object.keys(this.outputFormats),
156
+ mode: this.dryRun ? 'DRY_RUN' : 'LIVE'
157
+ });
158
+ }
159
+
160
+ /**
161
+ * Generate domain-specific secrets with environment coordination
162
+ * @param {string} domain - Domain name
163
+ * @param {string} environment - Environment (production, staging, development)
164
+ * @param {Object} options - Generation options
165
+ * @returns {Promise<Object>} Generated secrets and metadata
166
+ */
167
+ async generateDomainSpecificSecrets(domain, environment = 'production', options = {}) {
168
+ const {
169
+ customConfigs = {},
170
+ reuseExisting = true,
171
+ rotateAll = false,
172
+ formats = ['env', 'json', 'wrangler']
173
+ } = options;
174
+ console.log(`🔑 Generating secrets for ${domain} (${environment})`);
175
+ console.log(` 🔄 Reuse Existing: ${reuseExisting}`);
176
+ console.log(` 🔁 Rotate All: ${rotateAll}`);
177
+ console.log(` 📋 Formats: ${formats.join(', ')}`);
178
+ try {
179
+ // Load existing secrets if requested
180
+ let existingSecrets = {};
181
+ if (reuseExisting && !rotateAll) {
182
+ const existing = await this.loadDomainSecrets(domain, environment);
183
+ if (existing) {
184
+ existingSecrets = existing.secrets;
185
+ console.log(` 📂 Loaded ${Object.keys(existingSecrets).length} existing secrets`);
186
+ }
187
+ }
188
+
189
+ // Merge configurations
190
+ const configs = {
191
+ ...SECRET_CONFIGS,
192
+ ...customConfigs
193
+ };
194
+ const secrets = {
195
+ ...existingSecrets
196
+ };
197
+ let generated = 0;
198
+
199
+ // Generate missing or rotated secrets
200
+ for (const [key, config] of Object.entries(configs)) {
201
+ if (!secrets[key] || rotateAll) {
202
+ secrets[key] = this.generateSecretValue(config.length);
203
+ generated++;
204
+ }
205
+ }
206
+ console.log(` 🔑 Generated ${generated} new secrets, reused ${Object.keys(secrets).length - generated}`);
207
+
208
+ // Create secret metadata
209
+ const secretData = {
210
+ domain,
211
+ environment,
212
+ generated: new Date().toISOString(),
213
+ generatedBy: 'EnhancedSecretManager v2.0',
214
+ secretCount: Object.keys(secrets).length,
215
+ newSecrets: generated,
216
+ formats: formats,
217
+ ...secrets
218
+ };
219
+
220
+ // Save secrets
221
+ const savedFile = await this.saveDomainSecrets(domain, environment, secretData);
222
+
223
+ // Generate distribution files
224
+ const distribution = await this.generateSecretDistribution(domain, environment, secrets, formats);
225
+
226
+ // Deploy secrets if not dry run
227
+ let deployment = null;
228
+ if (!this.dryRun && options.deploy) {
229
+ deployment = await this.deploySecretsToCloudflare(domain, environment, secrets);
230
+ }
231
+
232
+ // Update domain tracking
233
+ this.domainSecrets.set(`${domain}-${environment}`, {
234
+ secrets,
235
+ generated: new Date(),
236
+ formats,
237
+ deployment
238
+ });
239
+ this.logSecretEvent('SECRETS_GENERATED', domain, {
240
+ environment,
241
+ secretCount: Object.keys(secrets).length,
242
+ newSecrets: generated,
243
+ formats
244
+ });
245
+ return {
246
+ domain,
247
+ environment,
248
+ secrets,
249
+ metadata: secretData,
250
+ savedFile,
251
+ distribution,
252
+ deployment,
253
+ summary: {
254
+ total: Object.keys(secrets).length,
255
+ generated,
256
+ reused: Object.keys(secrets).length - generated
257
+ }
258
+ };
259
+ } catch (error) {
260
+ this.logSecretEvent('SECRET_GENERATION_FAILED', domain, {
261
+ environment,
262
+ error: error.message
263
+ });
264
+ throw new Error(`Secret generation failed for ${domain}: ${error.message}`);
265
+ }
266
+ }
267
+
268
+ /**
269
+ * Coordinate secrets across multiple environments for a domain
270
+ * @param {string} domain - Domain name
271
+ * @param {Array} environments - Environments to coordinate
272
+ * @param {Object} options - Coordination options
273
+ * @returns {Promise<Object>} Coordination results
274
+ */
275
+ async coordinateSecretsAcrossEnvironments(domain, environments = ['development', 'staging', 'production'], options = {}) {
276
+ console.log(`🌐 Cross-Environment Secret Coordination for ${domain}`);
277
+ console.log(` 🌍 Environments: ${environments.join(', ')}`);
278
+ const {
279
+ syncCriticalSecrets = true,
280
+ generateUniquePerEnv = true,
281
+ formats = ['env', 'json', 'wrangler']
282
+ } = options;
283
+ const results = {
284
+ domain,
285
+ environments: {},
286
+ sharedSecrets: {},
287
+ coordinationId: this.generateCoordinationId(domain),
288
+ startTime: new Date()
289
+ };
290
+ try {
291
+ // Generate base secrets for production first
292
+ const productionSecrets = await this.generateDomainSpecificSecrets(domain, 'production', {
293
+ ...options,
294
+ formats
295
+ });
296
+ results.environments.production = productionSecrets;
297
+
298
+ // Extract critical secrets to share across environments
299
+ if (syncCriticalSecrets) {
300
+ const criticalConfigs = Object.entries(SECRET_CONFIGS).filter(([, config]) => config.scope === 'critical');
301
+ for (const [key] of criticalConfigs) {
302
+ if (productionSecrets.secrets[key]) {
303
+ results.sharedSecrets[key] = productionSecrets.secrets[key];
304
+ }
305
+ }
306
+ console.log(` 🔗 Sharing ${Object.keys(results.sharedSecrets).length} critical secrets across environments`);
307
+ }
308
+
309
+ // Process other environments
310
+ for (const env of environments) {
311
+ if (env === 'production') continue;
312
+ console.log(`\n 🌍 Processing ${env} environment...`);
313
+ const envOptions = {
314
+ ...options,
315
+ customConfigs: generateUniquePerEnv ? {} : results.sharedSecrets,
316
+ reuseExisting: !generateUniquePerEnv,
317
+ formats
318
+ };
319
+ const envSecrets = await this.generateDomainSpecificSecrets(domain, env, envOptions);
320
+
321
+ // Override with shared secrets if coordinating
322
+ if (syncCriticalSecrets) {
323
+ Object.assign(envSecrets.secrets, results.sharedSecrets);
324
+
325
+ // Re-save with coordinated secrets
326
+ await this.saveDomainSecrets(domain, env, {
327
+ ...envSecrets.metadata,
328
+ ...envSecrets.secrets,
329
+ coordinated: true,
330
+ coordinationId: results.coordinationId
331
+ });
332
+
333
+ // Re-generate distribution with coordinated secrets
334
+ envSecrets.distribution = await this.generateSecretDistribution(domain, env, envSecrets.secrets, formats);
335
+ }
336
+ results.environments[env] = envSecrets;
337
+ }
338
+ results.endTime = new Date();
339
+ results.duration = (results.endTime - results.startTime) / 1000;
340
+ this.logSecretEvent('CROSS_ENVIRONMENT_COORDINATION_COMPLETED', domain, {
341
+ coordinationId: results.coordinationId,
342
+ environments: environments,
343
+ sharedSecrets: Object.keys(results.sharedSecrets).length,
344
+ duration: results.duration
345
+ });
346
+ console.log(`\n✅ Cross-environment coordination completed (${results.duration.toFixed(1)}s)`);
347
+ console.log(` 🔗 Shared secrets: ${Object.keys(results.sharedSecrets).length}`);
348
+ console.log(` 🌍 Environments: ${Object.keys(results.environments).length}`);
349
+ return results;
350
+ } catch (error) {
351
+ this.logSecretEvent('CROSS_ENVIRONMENT_COORDINATION_FAILED', domain, {
352
+ coordinationId: results.coordinationId,
353
+ error: error.message
354
+ });
355
+ throw error;
356
+ }
357
+ }
358
+
359
+ /**
360
+ * Deploy secrets to Cloudflare Workers across environments
361
+ * @param {string} domain - Domain name
362
+ * @param {string} environment - Environment
363
+ * @param {Object} secrets - Secrets to deploy
364
+ * @returns {Promise<Object>} Deployment results
365
+ */
366
+ async deploySecretsToCloudflare(domain, environment, secrets) {
367
+ console.log(` ☁️ Deploying ${Object.keys(secrets).length} secrets to Cloudflare (${environment})...`);
368
+ if (this.dryRun) {
369
+ console.log(` 🔍 DRY RUN: Would deploy secrets to ${environment}`);
370
+ return {
371
+ status: 'dry-run',
372
+ secretCount: Object.keys(secrets).length
373
+ };
374
+ }
375
+ const results = {
376
+ domain,
377
+ environment,
378
+ deployed: [],
379
+ failed: [],
380
+ startTime: new Date()
381
+ };
382
+ try {
383
+ for (const [key, value] of Object.entries(secrets)) {
384
+ try {
385
+ await this.deploySingleSecret(key, value, environment);
386
+ results.deployed.push(key);
387
+ console.log(` ✅ ${key} deployed`);
388
+ } catch (error) {
389
+ results.failed.push({
390
+ key,
391
+ error: error.message
392
+ });
393
+ console.log(` ❌ ${key} failed: ${error.message}`);
394
+ }
395
+ }
396
+ results.endTime = new Date();
397
+ results.duration = (results.endTime - results.startTime) / 1000;
398
+ this.logSecretEvent('SECRETS_DEPLOYED', domain, {
399
+ environment,
400
+ deployed: results.deployed.length,
401
+ failed: results.failed.length,
402
+ duration: results.duration
403
+ });
404
+ return results;
405
+ } catch (error) {
406
+ this.logSecretEvent('SECRET_DEPLOYMENT_FAILED', domain, {
407
+ environment,
408
+ error: error.message
409
+ });
410
+ throw error;
411
+ }
412
+ }
413
+
414
+ /**
415
+ * Deploy single secret with retry logic
416
+ * @param {string} key - Secret key
417
+ * @param {string} value - Secret value
418
+ * @param {string} environment - Environment
419
+ */
420
+ async deploySingleSecret(key, value, environment) {
421
+ for (let attempt = 1; attempt <= this.retryAttempts; attempt++) {
422
+ try {
423
+ const command = `echo "${value}" | npx wrangler secret put ${key} --env ${environment}`;
424
+ execSync(command, {
425
+ shell: process.platform === 'win32' ? 'powershell.exe' : '/bin/bash',
426
+ stdio: 'pipe',
427
+ encoding: 'utf8',
428
+ timeout: 30000
429
+ });
430
+ return;
431
+ } catch (error) {
432
+ if (attempt === this.retryAttempts) {
433
+ throw error;
434
+ }
435
+ console.log(` ⚠️ Attempt ${attempt} failed for ${key}, retrying...`);
436
+ await new Promise(resolve => setTimeout(resolve, this.retryDelay));
437
+ }
438
+ }
439
+ }
440
+
441
+ /**
442
+ * Generate enhanced secret distribution files
443
+ * @param {string} domain - Domain name
444
+ * @param {string} environment - Environment
445
+ * @param {Object} secrets - Secrets to distribute
446
+ * @param {Array} formats - Output formats
447
+ * @returns {Promise<Object>} Distribution results
448
+ */
449
+ async generateSecretDistribution(domain, environment, secrets, formats) {
450
+ const distDir = join(this.secretPaths.distribution, domain, environment);
451
+ this.ensureDirectory(distDir);
452
+ const distribution = {
453
+ domain,
454
+ environment,
455
+ directory: distDir,
456
+ files: {},
457
+ formats: formats,
458
+ generated: new Date()
459
+ };
460
+ console.log(` 📤 Generating distribution files in ${formats.join(', ')} formats...`);
461
+ try {
462
+ for (const format of formats) {
463
+ if (this.outputFormats[format]) {
464
+ const file = await this.generateFormatFile(distDir, domain, environment, secrets, format);
465
+ distribution.files[format] = file;
466
+ console.log(` 📄 ${format}: ${file.filename}`);
467
+ }
468
+ }
469
+
470
+ // Generate comprehensive README
471
+ const readme = await this.generateDistributionReadme(domain, environment, secrets, formats);
472
+ distribution.files.readme = readme;
473
+ this.logSecretEvent('DISTRIBUTION_GENERATED', domain, {
474
+ environment,
475
+ formats,
476
+ fileCount: Object.keys(distribution.files).length
477
+ });
478
+ return distribution;
479
+ } catch (error) {
480
+ this.logSecretEvent('DISTRIBUTION_GENERATION_FAILED', domain, {
481
+ environment,
482
+ error: error.message
483
+ });
484
+ throw error;
485
+ }
486
+ }
487
+
488
+ /**
489
+ * Generate specific format file
490
+ * @param {string} distDir - Distribution directory
491
+ * @param {string} domain - Domain name
492
+ * @param {string} environment - Environment
493
+ * @param {Object} secrets - Secrets
494
+ * @param {string} format - Format type
495
+ * @returns {Promise<Object>} File information
496
+ */
497
+ async generateFormatFile(distDir, domain, environment, secrets, format) {
498
+ const formatConfig = this.outputFormats[format];
499
+ const filename = `secrets-${environment}${formatConfig.extension}`;
500
+ const filepath = join(distDir, filename);
501
+ let content = '';
502
+ switch (format) {
503
+ case 'env':
504
+ content = Object.entries(secrets).map(([key, value]) => `${key}=${value}`).join('\n');
505
+ break;
506
+ case 'json':
507
+ content = JSON.stringify(secrets, null, 2);
508
+ break;
509
+ case 'wrangler':
510
+ content = ['#!/bin/bash', `# Wrangler secret deployment for ${domain} (${environment})`, `# Generated: ${new Date().toISOString()}`, '', ...Object.entries(secrets).map(([key, value]) => `echo "${value}" | npx wrangler secret put ${key} --env ${environment}`)].join('\n');
511
+ break;
512
+ case 'powershell':
513
+ content = [`# PowerShell secret deployment for ${domain} (${environment})`, `# Generated: ${new Date().toISOString()}`, '', ...Object.entries(secrets).map(([key, value]) => `"${value}" | npx wrangler secret put ${key} --env ${environment}`)].join('\n');
514
+ break;
515
+ case 'docker':
516
+ content = Object.entries(secrets).map(([key, value]) => `${key}=${value}`).join('\n');
517
+ break;
518
+ case 'kubernetes':
519
+ {
520
+ const encodedSecrets = {};
521
+ Object.entries(secrets).forEach(([key, value]) => {
522
+ encodedSecrets[key] = Buffer.from(value).toString('base64');
523
+ });
524
+ content = ['apiVersion: v1', 'kind: Secret', 'metadata:', ` name: ${domain.replace(/\./g, '-')}-secrets-${environment}`, ` namespace: ${environment}`, 'type: Opaque', 'data:', ...Object.entries(encodedSecrets).map(([key, value]) => ` ${key}: ${value}`)].join('\n');
525
+ break;
526
+ }
527
+ default:
528
+ throw new Error(`Unsupported format: ${format}`);
529
+ }
530
+ writeFileSync(filepath, content);
531
+ return {
532
+ format,
533
+ filename,
534
+ filepath,
535
+ description: formatConfig.description,
536
+ size: content.length
537
+ };
538
+ }
539
+
540
+ /**
541
+ * Generate comprehensive distribution README
542
+ * @param {string} domain - Domain name
543
+ * @param {string} environment - Environment
544
+ * @param {Object} secrets - Secrets
545
+ * @param {Array} formats - Formats
546
+ * @returns {Promise<Object>} README file info
547
+ */
548
+ async generateDistributionReadme(domain, environment, secrets, formats) {
549
+ const readmeContent = `# Secret Distribution for ${domain} (${environment})
550
+
551
+ Generated: ${new Date().toISOString()}
552
+ Secret Count: ${Object.keys(secrets).length}
553
+ Formats: ${formats.join(', ')}
554
+
555
+ ## 🔐 Generated Files
556
+
557
+ ${formats.map(format => {
558
+ const config = this.outputFormats[format];
559
+ const filename = `secrets-${environment}${config.extension}`;
560
+ return `- **${filename}** - ${config.description}`;
561
+ }).join('\n')}
562
+
563
+ ## 🚀 Usage Instructions
564
+
565
+ ### Environment Variables (.env)
566
+ \`\`\`bash
567
+ # For Node.js applications
568
+ cp secrets-${environment}.env /path/to/your/app/.env
569
+ source secrets-${environment}.env
570
+ \`\`\`
571
+
572
+ ### JSON Format
573
+ \`\`\`javascript
574
+ // For API consumption
575
+ const secrets = require('./secrets-${environment}.json');
576
+ console.log(secrets.AUTH_JWT_SECRET);
577
+ \`\`\`
578
+
579
+ ### Cloudflare Workers Deployment
580
+ \`\`\`bash
581
+ # Linux/Mac
582
+ chmod +x secrets-${environment}.sh
583
+ ./secrets-${environment}.sh
584
+
585
+ # Windows PowerShell
586
+ .\\secrets-${environment}.ps1
587
+ \`\`\`
588
+
589
+ ### Docker Deployment
590
+ \`\`\`bash
591
+ # Use as environment file
592
+ docker run --env-file secrets-${environment}.env your-image
593
+ \`\`\`
594
+
595
+ ### Kubernetes Deployment
596
+ \`\`\`bash
597
+ # Apply secret manifest
598
+ kubectl apply -f secrets-${environment}.yaml
599
+ \`\`\`
600
+
601
+ ## 🛡️ Security Guidelines
602
+
603
+ - **Keep secrets secure** - Never commit to version control
604
+ - **Use secure channels** - Distribute via encrypted channels only
605
+ - **Rotate regularly** - Update secrets according to security policy
606
+ - **Monitor access** - Track secret usage and access patterns
607
+ - **Backup safely** - Store backups in encrypted, access-controlled locations
608
+
609
+ ## 🔄 Secret Rotation
610
+
611
+ To rotate secrets for this domain and environment:
612
+ \`\`\`bash
613
+ # Rotate all secrets
614
+ node scripts/shared/secret-generator.js --domain ${domain} --environment ${environment} --rotate-all
615
+
616
+ # Rotate specific secret
617
+ node scripts/shared/secret-generator.js --domain ${domain} --environment ${environment} --rotate AUTH_JWT_SECRET
618
+ \`\`\`
619
+
620
+ ## 📊 Secret Inventory
621
+
622
+ ${Object.entries(SECRET_CONFIGS).map(([key, config]) => `- **${key}**: ${config.description} (${config.scope})`).join('\n')}
623
+
624
+ ---
625
+ *Generated by Enhanced Secret Manager v2.0*
626
+ *For support: Check project documentation*
627
+ `;
628
+ const readmeFile = join(domain, environment, 'README.md');
629
+ const readmePath = join(this.secretPaths.distribution, readmeFile);
630
+ writeFileSync(readmePath, readmeContent);
631
+ return {
632
+ filename: 'README.md',
633
+ filepath: readmePath,
634
+ size: readmeContent.length
635
+ };
636
+ }
637
+
638
+ // Utility methods
639
+
640
+ generateSecretValue(length) {
641
+ return randomBytes(length / 2).toString('hex');
642
+ }
643
+ generateCoordinationId(domain) {
644
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
645
+ const hash = createHash('md5').update(domain).digest('hex').substring(0, 8);
646
+ return `coord-${domain}-${hash}-${timestamp}`;
647
+ }
648
+ async loadDomainSecrets(domain, environment) {
649
+ const filename = join(this.secretPaths.root, `${domain}-${environment}-secrets.json`);
650
+ if (!existsSync(filename)) {
651
+ return null;
652
+ }
653
+ try {
654
+ const data = JSON.parse(readFileSync(filename, 'utf8'));
655
+ const {
656
+ domain: d,
657
+ environment: e,
658
+ generated,
659
+ generatedBy,
660
+ secretCount,
661
+ newSecrets,
662
+ formats,
663
+ ...secrets
664
+ } = data;
665
+ return {
666
+ secrets,
667
+ metadata: {
668
+ domain: d,
669
+ environment: e,
670
+ generated,
671
+ generatedBy,
672
+ secretCount,
673
+ newSecrets,
674
+ formats
675
+ },
676
+ filename
677
+ };
678
+ } catch (error) {
679
+ throw new Error(`Failed to load secrets for ${domain} (${environment}): ${error.message}`);
680
+ }
681
+ }
682
+ async saveDomainSecrets(domain, environment, secretData) {
683
+ const filename = join(this.secretPaths.root, `${domain}-${environment}-secrets.json`);
684
+ if (this.dryRun) {
685
+ console.log(` 🔍 DRY RUN: Would save secrets to ${filename}`);
686
+ return filename;
687
+ }
688
+ writeFileSync(filename, JSON.stringify(secretData, null, 2));
689
+ console.log(` 💾 Secrets saved: ${filename}`);
690
+ return filename;
691
+ }
692
+ ensureDirectory(path) {
693
+ if (!existsSync(path)) {
694
+ mkdirSync(path, {
695
+ recursive: true
696
+ });
697
+ }
698
+ }
699
+ logSecretEvent(event, domain, details = {}) {
700
+ const logEntry = {
701
+ timestamp: new Date().toISOString(),
702
+ event,
703
+ domain,
704
+ details,
705
+ user: process.env.USER || process.env.USERNAME || 'system'
706
+ };
707
+ try {
708
+ const logLine = JSON.stringify(logEntry) + '\n';
709
+ if (existsSync(this.secretPaths.audit)) {
710
+ appendFileSync(this.secretPaths.audit, logLine);
711
+ } else {
712
+ writeFileSync(this.secretPaths.audit, logLine);
713
+ }
714
+ } catch (error) {
715
+ console.warn(`⚠️ Failed to log secret event: ${error.message}`);
716
+ }
717
+ }
718
+ }
719
+
720
+ // Legacy function exports for backward compatibility
721
+ export function generateSecrets(customConfigs = {}) {
722
+ const configs = {
723
+ ...SECRET_CONFIGS,
724
+ ...customConfigs
725
+ };
726
+ const secrets = {};
727
+ for (const [key, config] of Object.entries(configs)) {
728
+ secrets[key] = randomBytes(config.length / 2).toString('hex');
729
+ }
730
+ return secrets;
731
+ }
732
+ export function generateSingleSecret(length = 32) {
733
+ return randomBytes(length / 2).toString('hex');
734
+ }
735
+ export function saveSecrets(domain, environment, secrets, additionalData = {}) {
736
+ const secretsDir = 'secrets';
737
+ if (!existsSync(secretsDir)) {
738
+ mkdirSync(secretsDir, {
739
+ recursive: true
740
+ });
741
+ }
742
+ const data = {
743
+ domain,
744
+ environment,
745
+ generated: new Date().toISOString(),
746
+ note: 'Generated by modular secret system',
747
+ ...additionalData,
748
+ ...secrets
749
+ };
750
+ const filename = join(secretsDir, `${domain}-secrets.json`);
751
+ writeFileSync(filename, JSON.stringify(data, null, 2));
752
+ return filename;
753
+ }
754
+ export function loadSecrets(domain) {
755
+ const filename = join('secrets', `${domain}-secrets.json`);
756
+ if (!existsSync(filename)) {
757
+ return null;
758
+ }
759
+ try {
760
+ const data = JSON.parse(readFileSync(filename, 'utf8'));
761
+ const {
762
+ domain: d,
763
+ environment,
764
+ generated,
765
+ note,
766
+ ...secrets
767
+ } = data;
768
+ return {
769
+ secrets,
770
+ metadata: {
771
+ domain: d,
772
+ environment,
773
+ generated,
774
+ note
775
+ },
776
+ filename
777
+ };
778
+ } catch (error) {
779
+ throw new Error(`Failed to load secrets: ${error.message}`);
780
+ }
781
+ }
782
+ export function distributeSecrets(domain, secrets) {
783
+ const distDir = join('secrets', 'distribution', domain);
784
+ mkdirSync(distDir, {
785
+ recursive: true
786
+ });
787
+ const files = {};
788
+
789
+ // .env format
790
+ const envContent = Object.entries(secrets).map(([key, value]) => `${key}=${value}`).join('\n');
791
+ const envFile = join(distDir, '.env');
792
+ writeFileSync(envFile, envContent);
793
+ files.env = envFile;
794
+
795
+ // JSON format
796
+ const jsonFile = join(distDir, 'secrets.json');
797
+ writeFileSync(jsonFile, JSON.stringify(secrets, null, 2));
798
+ files.json = jsonFile;
799
+
800
+ // Shell script format (cross-platform)
801
+ const shellContent = Object.entries(secrets).map(([key, value]) => `echo "${value}" | npx wrangler secret put ${key} --env production`).join('\n');
802
+ const shellFile = join(distDir, 'deploy-secrets.sh');
803
+ writeFileSync(shellFile, shellContent);
804
+ files.shell = shellFile;
805
+
806
+ // PowerShell script format
807
+ const psContent = Object.entries(secrets).map(([key, value]) => `"${value}" | npx wrangler secret put ${key} --env production`).join('\n');
808
+ const psFile = join(distDir, 'deploy-secrets.ps1');
809
+ writeFileSync(psFile, psContent);
810
+ files.powershell = psFile;
811
+
812
+ // README
813
+ const readme = `# Secret Distribution for ${domain}
814
+
815
+ Generated: ${new Date().toISOString()}
816
+
817
+ ## Files
818
+ - \`.env\` - Environment variables for Node.js applications
819
+ - \`secrets.json\` - JSON format for API consumption
820
+ - \`deploy-secrets.sh\` - Bash commands for Cloudflare Workers
821
+ - \`deploy-secrets.ps1\` - PowerShell commands for Cloudflare Workers
822
+
823
+ ## Usage
824
+
825
+ ### For downstream Node.js applications:
826
+ \`\`\`bash
827
+ cp .env /path/to/your/app/
828
+ \`\`\`
829
+
830
+ ### For upstream Cloudflare Workers (Linux/Mac):
831
+ \`\`\`bash
832
+ chmod +x deploy-secrets.sh
833
+ ./deploy-secrets.sh
834
+ \`\`\`
835
+
836
+ ### For upstream Cloudflare Workers (Windows):
837
+ \`\`\`powershell
838
+ .\\deploy-secrets.ps1
839
+ \`\`\`
840
+
841
+ ## Security Notice
842
+ - Keep these files secure
843
+ - Never commit to version control
844
+ - Distribute via secure channels only
845
+ `;
846
+ const readmeFile = join(distDir, 'README.md');
847
+ writeFileSync(readmeFile, readme);
848
+ files.readme = readmeFile;
849
+ return {
850
+ directory: distDir,
851
+ files
852
+ };
853
+ }
854
+ export function validateSecrets(secrets) {
855
+ const issues = [];
856
+ for (const [key, value] of Object.entries(secrets)) {
857
+ if (!value || typeof value !== 'string') {
858
+ issues.push(`${key}: Invalid value`);
859
+ continue;
860
+ }
861
+ if (value.length < 16) {
862
+ issues.push(`${key}: Too short (minimum 16 characters)`);
863
+ }
864
+ if (!/^[a-f0-9]+$/i.test(value)) {
865
+ issues.push(`${key}: Should be hexadecimal`);
866
+ }
867
+ }
868
+ return issues;
869
+ }
870
+ export function listSecretsFiles() {
871
+ const secretsDir = 'secrets';
872
+ if (!existsSync(secretsDir)) {
873
+ return [];
874
+ }
875
+ try {
876
+ const files = [];
877
+ const items = readdirSync(secretsDir);
878
+ for (const item of items) {
879
+ if (item.endsWith('-secrets.json')) {
880
+ const domain = item.replace('-secrets.json', '');
881
+ const filepath = join(secretsDir, item);
882
+ const stat = statSync(filepath);
883
+ files.push({
884
+ domain,
885
+ filepath,
886
+ modified: stat.mtime,
887
+ size: stat.size
888
+ });
889
+ }
890
+ }
891
+ return files.sort((a, b) => b.modified - a.modified);
892
+ } catch (error) {
893
+ return [];
894
+ }
895
+ }
896
+ export { SECRET_CONFIGS };