@tamyla/clodo-framework 3.1.10 → 3.1.11

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