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