@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,562 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Domain Discovery Module
5
+ * Enterprise-grade runtime domain configuration discovery and management
6
+ *
7
+ * Extracted from dynamic-config-builder.js with enhancements
8
+ */
9
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from 'fs';
10
+ import { join, dirname } from 'path';
11
+ import { fileURLToPath } from 'url';
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = dirname(__filename);
14
+
15
+ /**
16
+ * Advanced Domain Discovery Manager
17
+ * Discovers and manages domain configurations across Cloudflare infrastructure
18
+ */
19
+ export class DomainDiscovery {
20
+ constructor(options = {}) {
21
+ this.apiToken = options.apiToken;
22
+ this.timeout = options.timeout || 30000;
23
+ this.retryAttempts = options.retryAttempts || 3;
24
+ this.retryDelay = options.retryDelay || 2000;
25
+
26
+ // Configuration cache and templates
27
+ this.configCache = new Map();
28
+ this.discoveredConfigs = new Map();
29
+ this.templates = new Map();
30
+
31
+ // Paths for configuration management
32
+ this.configPaths = {
33
+ domains: join(__dirname, '..', '..', 'src', 'config', 'domains.js'),
34
+ runtime: join(__dirname, '..', '..', 'runtime-config'),
35
+ templates: join(__dirname, '..', '..', 'config-templates'),
36
+ cache: join(__dirname, '..', '..', '.config-cache')
37
+ };
38
+
39
+ // Ensure directories exist
40
+ Object.values(this.configPaths).forEach(path => {
41
+ if (path.endsWith('.js')) return; // Skip files
42
+ if (!existsSync(path)) {
43
+ mkdirSync(path, {
44
+ recursive: true
45
+ });
46
+ }
47
+ });
48
+ this.initializeDiscovery();
49
+ }
50
+
51
+ /**
52
+ * Initialize discovery system
53
+ */
54
+ initializeDiscovery() {
55
+ console.log('🔍 Domain Discovery System v1.0');
56
+ console.log('===============================');
57
+ console.log(`⚙️ API Token: ${this.apiToken ? 'Configured' : 'Not set'}`);
58
+ console.log(`📁 Cache Directory: ${this.configPaths.cache}`);
59
+ console.log(`🔄 Retry Attempts: ${this.retryAttempts}`);
60
+ console.log('');
61
+
62
+ // Load cached configurations
63
+ this.loadConfigCache();
64
+
65
+ // Load configuration templates
66
+ this.loadConfigTemplates();
67
+ }
68
+
69
+ /**
70
+ * Discover complete domain configuration from Cloudflare
71
+ * @param {string} domainName - Domain to discover
72
+ * @param {string} apiToken - Cloudflare API token (optional if set in constructor)
73
+ * @returns {Promise<Object>} Complete domain configuration
74
+ */
75
+ async discoverDomainConfig(domainName, apiToken = null) {
76
+ const token = apiToken || this.apiToken;
77
+ if (!token) {
78
+ throw new Error('Cloudflare API token is required for domain discovery');
79
+ }
80
+ console.log(`🔍 Discovering configuration for ${domainName}...`);
81
+
82
+ // Check cache first
83
+ const cacheKey = `${domainName}-${this.getCacheKeyHash(token)}`;
84
+ if (this.configCache.has(cacheKey)) {
85
+ const cached = this.configCache.get(cacheKey);
86
+ const age = Date.now() - cached.timestamp;
87
+
88
+ // Use cached config if less than 1 hour old
89
+ if (age < 3600000) {
90
+ console.log(` 📋 Using cached configuration (${Math.round(age / 60000)}m old)`);
91
+ return cached.config;
92
+ }
93
+ }
94
+ try {
95
+ // Discovery process
96
+ const discoveredConfig = await this.performDomainDiscovery(domainName, token);
97
+
98
+ // Cache the result
99
+ this.cacheConfiguration(cacheKey, discoveredConfig);
100
+
101
+ // Store in discovered configs
102
+ this.discoveredConfigs.set(domainName, discoveredConfig);
103
+ console.log(`✅ Configuration discovered for ${domainName}`);
104
+ console.log(` 📊 Account: ${discoveredConfig.accountName}`);
105
+ console.log(` 🌐 Zone: ${discoveredConfig.zoneName}`);
106
+ console.log(` 🆔 Zone ID: ${discoveredConfig.zoneId}`);
107
+ return discoveredConfig;
108
+ } catch (error) {
109
+ console.error(`❌ Failed to discover configuration for ${domainName}:`, error.message);
110
+ throw new Error(`Domain discovery failed: ${error.message}`);
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Perform the actual domain discovery process
116
+ * @param {string} domainName - Domain name
117
+ * @param {string} apiToken - API token
118
+ * @returns {Promise<Object>} Discovered configuration
119
+ */
120
+ async performDomainDiscovery(domainName, apiToken) {
121
+ // Step 1: Get Cloudflare accounts
122
+ const accounts = await this.fetchCloudflareAccounts(apiToken);
123
+ if (!accounts.length) {
124
+ throw new Error('No Cloudflare accounts found');
125
+ }
126
+
127
+ // Use first account or find specific one
128
+ const account = accounts[0];
129
+ console.log(` ✅ Found account: ${account.name} (${account.id})`);
130
+
131
+ // Step 2: Get zones for account
132
+ const zones = await this.fetchAccountZones(account.id, apiToken);
133
+
134
+ // Step 3: Find zone for domain
135
+ const zone = zones.find(z => z.name === domainName || domainName.endsWith(z.name));
136
+ if (!zone) {
137
+ throw new Error(`No zone found for domain: ${domainName}. Available zones: ${zones.map(z => z.name).join(', ')}`);
138
+ }
139
+ console.log(` ✅ Found zone: ${zone.name} (${zone.id})`);
140
+
141
+ // Step 4: Build comprehensive configuration
142
+ const discoveredConfig = await this.buildDomainConfiguration(domainName, account, zone, apiToken);
143
+ return discoveredConfig;
144
+ }
145
+
146
+ /**
147
+ * Fetch Cloudflare accounts
148
+ * @param {string} apiToken - API token
149
+ * @returns {Promise<Array>} Account list
150
+ */
151
+ async fetchCloudflareAccounts(apiToken) {
152
+ console.log(' 🔍 Fetching Cloudflare accounts...');
153
+ const response = await this.makeCloudflareRequest('https://api.cloudflare.com/client/v4/accounts', apiToken);
154
+ if (!response.success || !response.result.length) {
155
+ throw new Error('No Cloudflare accounts accessible with this token');
156
+ }
157
+ return response.result;
158
+ }
159
+
160
+ /**
161
+ * Fetch zones for account
162
+ * @param {string} accountId - Account ID
163
+ * @param {string} apiToken - API token
164
+ * @returns {Promise<Array>} Zone list
165
+ */
166
+ async fetchAccountZones(accountId, apiToken) {
167
+ console.log(' 🌐 Fetching account zones...');
168
+ const response = await this.makeCloudflareRequest(`https://api.cloudflare.com/client/v4/zones?account.id=${accountId}`, apiToken);
169
+ if (!response.success) {
170
+ throw new Error('Failed to fetch zones for account');
171
+ }
172
+ return response.result;
173
+ }
174
+
175
+ /**
176
+ * Build complete domain configuration
177
+ * @param {string} domainName - Domain name
178
+ * @param {Object} account - Cloudflare account
179
+ * @param {Object} zone - Cloudflare zone
180
+ * @param {string} apiToken - API token
181
+ * @returns {Promise<Object>} Complete configuration
182
+ */
183
+ async buildDomainConfiguration(domainName, account, zone, apiToken) {
184
+ const cleanDomainName = domainName.replace('.com', '').replace(/[^a-zA-Z0-9]/g, '');
185
+ const config = {
186
+ // Basic domain information
187
+ name: cleanDomainName,
188
+ displayName: this.capitalizeFirst(cleanDomainName),
189
+ fullDomain: domainName,
190
+ // Cloudflare infrastructure
191
+ accountId: account.id,
192
+ accountName: account.name,
193
+ zoneId: zone.id,
194
+ zoneName: zone.name,
195
+ // Discovery metadata
196
+ discoveredAt: new Date().toISOString(),
197
+ discoveryVersion: '1.0',
198
+ // Multi-environment domains
199
+ domains: {
200
+ production: domainName,
201
+ staging: `staging.${domainName}`,
202
+ development: `dev.${domainName}`
203
+ },
204
+ // Service URLs
205
+ services: {
206
+ frontend: {
207
+ production: `https://${domainName}`,
208
+ staging: `https://staging.${domainName}`,
209
+ development: `https://dev.${domainName}`
210
+ },
211
+ api: {
212
+ production: `https://api.${domainName}`,
213
+ staging: `https://api-staging.${domainName}`,
214
+ development: `https://api-dev.${domainName}`
215
+ },
216
+ auth: {
217
+ production: `https://auth.${domainName}`,
218
+ staging: `https://auth-staging.${domainName}`,
219
+ development: `https://auth-dev.${domainName}`
220
+ }
221
+ },
222
+ // CORS configuration
223
+ corsOrigins: {
224
+ production: [`https://${domainName}`, `https://*.${domainName}`],
225
+ staging: [`https://staging.${domainName}`, `https://*.staging.${domainName}`],
226
+ development: [`http://localhost:*`, `https://dev.${domainName}`]
227
+ },
228
+ // Database configuration
229
+ databases: {
230
+ production: {
231
+ name: `${cleanDomainName}-auth-db`,
232
+ id: null,
233
+ binding: 'DB'
234
+ },
235
+ staging: {
236
+ name: `${cleanDomainName}-auth-db-staging`,
237
+ id: null,
238
+ binding: 'DB'
239
+ },
240
+ development: {
241
+ name: `${cleanDomainName}-auth-db-local`,
242
+ id: null,
243
+ binding: 'DB'
244
+ }
245
+ },
246
+ // Worker configuration
247
+ workers: {
248
+ production: {
249
+ name: `${cleanDomainName}-data-service`,
250
+ routes: [`${domainName}/api/*`, `api.${domainName}/*`]
251
+ },
252
+ staging: {
253
+ name: `${cleanDomainName}-data-service-staging`,
254
+ routes: [`staging.${domainName}/api/*`, `api-staging.${domainName}/*`]
255
+ },
256
+ development: {
257
+ name: `${cleanDomainName}-data-service-dev`,
258
+ routes: []
259
+ }
260
+ },
261
+ // Feature flags
262
+ features: {
263
+ magicLinkAuth: true,
264
+ fileStorage: true,
265
+ userProfiles: true,
266
+ logging: true,
267
+ webhooks: true,
268
+ enhancedFramework: true,
269
+ crossDomainAuth: true
270
+ },
271
+ // Application settings
272
+ settings: {
273
+ magicLinkExpiryMinutes: 15,
274
+ rateLimitWindowMs: 900000,
275
+ rateLimitMax: 100,
276
+ maxFileSize: 26214400,
277
+ allowedFileTypes: ['image/jpeg', 'image/png', 'image/webp', 'image/gif', 'application/pdf', 'video/mp4', 'video/webm', 'audio/mpeg', 'audio/wav', 'text/plain', 'application/json'],
278
+ sessionExpiryHours: 24,
279
+ maxConcurrentSessions: 5
280
+ }
281
+ };
282
+
283
+ // Discover existing infrastructure if available
284
+ await this.discoverExistingInfrastructure(config, apiToken);
285
+ return config;
286
+ }
287
+
288
+ /**
289
+ * Discover existing Cloudflare infrastructure for domain
290
+ * @param {Object} config - Configuration to enhance
291
+ * @param {string} apiToken - API token
292
+ */
293
+ async discoverExistingInfrastructure(config, apiToken) {
294
+ try {
295
+ console.log(' 🔍 Discovering existing infrastructure...');
296
+
297
+ // Discover D1 databases
298
+ await this.discoverD1Databases(config, apiToken);
299
+
300
+ // Discover Workers
301
+ await this.discoverWorkers(config, apiToken);
302
+
303
+ // Discover KV namespaces (if needed)
304
+ await this.discoverKVNamespaces(config, apiToken);
305
+ } catch (error) {
306
+ console.log(` ⚠️ Infrastructure discovery failed: ${error.message}`);
307
+ }
308
+ }
309
+
310
+ /**
311
+ * Discover existing D1 databases
312
+ * @param {Object} config - Configuration object
313
+ * @param {string} apiToken - API token
314
+ */
315
+ async discoverD1Databases(config, apiToken) {
316
+ try {
317
+ const response = await this.makeCloudflareRequest(`https://api.cloudflare.com/client/v4/accounts/${config.accountId}/d1/database`, apiToken);
318
+ if (response.success) {
319
+ const databases = response.result;
320
+
321
+ // Match databases by name
322
+ Object.keys(config.databases).forEach(env => {
323
+ const expectedName = config.databases[env].name;
324
+ const foundDb = databases.find(db => db.name === expectedName);
325
+ if (foundDb) {
326
+ config.databases[env].id = foundDb.uuid;
327
+ console.log(` 📋 Found ${env} database: ${foundDb.name}`);
328
+ }
329
+ });
330
+ }
331
+ } catch (error) {
332
+ // Handle different error types gracefully
333
+ if (error.message.includes('401') || error.message.includes('Unauthorized')) {
334
+ console.log(` ℹ️ D1 database discovery requires additional API token permissions`);
335
+ console.log(` 💡 To enable D1 discovery, ensure your API token has 'Account:Read' or 'Cloudflare D1:Edit' permissions`);
336
+ } else {
337
+ console.log(` ⚠️ D1 database discovery failed: ${error.message}`);
338
+ }
339
+ }
340
+ }
341
+
342
+ /**
343
+ * Discover existing Workers
344
+ * @param {Object} config - Configuration object
345
+ * @param {string} apiToken - API token
346
+ */
347
+ async discoverWorkers(config, apiToken) {
348
+ try {
349
+ const response = await this.makeCloudflareRequest(`https://api.cloudflare.com/client/v4/accounts/${config.accountId}/workers/scripts`, apiToken);
350
+ if (response.success) {
351
+ const workers = response.result;
352
+
353
+ // Match workers by name
354
+ Object.keys(config.workers).forEach(env => {
355
+ const expectedName = config.workers[env].name;
356
+ const foundWorker = workers.find(w => w.id === expectedName);
357
+ if (foundWorker) {
358
+ config.workers[env].exists = true;
359
+ config.workers[env].createdAt = foundWorker.created_on;
360
+ console.log(` ⚡ Found ${env} worker: ${foundWorker.id}`);
361
+ }
362
+ });
363
+ }
364
+ } catch (error) {
365
+ console.log(` ⚠️ Worker discovery failed: ${error.message}`);
366
+ }
367
+ }
368
+
369
+ /**
370
+ * Discover KV namespaces (placeholder for future enhancement)
371
+ * @param {Object} config - Configuration object
372
+ * @param {string} apiToken - API token
373
+ */
374
+ async discoverKVNamespaces(config, apiToken) {
375
+ // Placeholder for KV namespace discovery if needed
376
+ console.log(' 🗂️ KV namespace discovery (not implemented)');
377
+ }
378
+
379
+ /**
380
+ * Generate domain configuration from template
381
+ * @param {string} domainName - Domain name
382
+ * @param {string} templateName - Template name
383
+ * @returns {Object} Generated configuration
384
+ */
385
+ generateConfigFromTemplate(domainName, templateName = 'standard') {
386
+ console.log(`🏗️ Generating configuration for ${domainName} using template: ${templateName}`);
387
+ const template = this.templates.get(templateName) || this.getStandardTemplate();
388
+ const cleanDomainName = domainName.replace('.com', '').replace(/[^a-zA-Z0-9]/g, '');
389
+
390
+ // Replace template variables
391
+ const configStr = JSON.stringify(template).replace(/{DOMAIN_NAME}/g, domainName).replace(/{CLEAN_DOMAIN_NAME}/g, cleanDomainName).replace(/{DISPLAY_NAME}/g, this.capitalizeFirst(cleanDomainName)).replace(/{TIMESTAMP}/g, new Date().toISOString());
392
+ const config = JSON.parse(configStr);
393
+ console.log(`✅ Configuration generated from template`);
394
+ return config;
395
+ }
396
+
397
+ /**
398
+ * Validate domain configuration
399
+ * @param {string} domainName - Domain name
400
+ * @param {Object} config - Configuration to validate
401
+ * @returns {Object} Validation result
402
+ */
403
+ validateDomainConfig(domainName, config) {
404
+ console.log(`🔍 Validating configuration for ${domainName}...`);
405
+ const errors = [];
406
+ const warnings = [];
407
+
408
+ // Required fields validation
409
+ if (!config.accountId) errors.push('Missing Cloudflare account ID');
410
+ if (!config.zoneId) errors.push('Missing Cloudflare zone ID');
411
+ if (!config.domains?.production) errors.push('Missing production domain URL');
412
+
413
+ // Services validation
414
+ const requiredServices = ['frontend', 'api', 'auth'];
415
+ for (const service of requiredServices) {
416
+ if (!config.services?.[service]?.production) {
417
+ errors.push(`Missing ${service} production URL`);
418
+ }
419
+ }
420
+
421
+ // Database validation
422
+ const environments = ['production', 'staging', 'development'];
423
+ environments.forEach(env => {
424
+ if (!config.databases?.[env]?.name) {
425
+ errors.push(`Missing ${env} database name`);
426
+ }
427
+ });
428
+
429
+ // Worker validation
430
+ environments.forEach(env => {
431
+ if (!config.workers?.[env]?.name) {
432
+ errors.push(`Missing ${env} worker name`);
433
+ }
434
+ });
435
+
436
+ // CORS validation
437
+ if (!config.corsOrigins?.production?.length) {
438
+ warnings.push('No CORS origins configured for production');
439
+ }
440
+
441
+ // Feature validation
442
+ if (!config.features) {
443
+ warnings.push('No feature flags configured');
444
+ }
445
+ const result = {
446
+ valid: errors.length === 0,
447
+ errors,
448
+ warnings,
449
+ score: this.calculateConfigScore(config, errors, warnings)
450
+ };
451
+ if (result.valid) {
452
+ console.log(`✅ Configuration validation passed (Score: ${result.score}/100)`);
453
+ } else {
454
+ console.log(`❌ Configuration validation failed (${errors.length} errors, ${warnings.length} warnings)`);
455
+ }
456
+ return result;
457
+ }
458
+
459
+ /**
460
+ * Calculate configuration quality score
461
+ * @param {Object} config - Configuration
462
+ * @param {Array} errors - Validation errors
463
+ * @param {Array} warnings - Validation warnings
464
+ * @returns {number} Quality score (0-100)
465
+ */
466
+ calculateConfigScore(config, errors, warnings) {
467
+ let score = 100;
468
+
469
+ // Deduct for errors and warnings
470
+ score -= errors.length * 20;
471
+ score -= warnings.length * 5;
472
+
473
+ // Bonus for completeness
474
+ if (config.databases?.production?.id) score += 5;
475
+ if (config.workers?.production?.exists) score += 5;
476
+ if (config.features && Object.keys(config.features).length > 5) score += 5;
477
+ if (config.settings && Object.keys(config.settings).length > 5) score += 5;
478
+ return Math.max(0, Math.min(100, score));
479
+ }
480
+
481
+ // Utility methods
482
+
483
+ async makeCloudflareRequest(url, apiToken) {
484
+ for (let attempt = 1; attempt <= this.retryAttempts; attempt++) {
485
+ try {
486
+ const response = await fetch(url, {
487
+ headers: {
488
+ 'Authorization': `Bearer ${apiToken}`,
489
+ 'Content-Type': 'application/json'
490
+ }
491
+ });
492
+ if (!response.ok) {
493
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
494
+ }
495
+ return await response.json();
496
+ } catch (error) {
497
+ if (attempt === this.retryAttempts) {
498
+ throw error;
499
+ }
500
+ console.log(` ⚠️ Request attempt ${attempt} failed, retrying...`);
501
+ await new Promise(resolve => setTimeout(resolve, this.retryDelay));
502
+ }
503
+ }
504
+ }
505
+ cacheConfiguration(key, config) {
506
+ this.configCache.set(key, {
507
+ config,
508
+ timestamp: Date.now()
509
+ });
510
+
511
+ // Save to disk cache
512
+ const cacheFile = join(this.configPaths.cache, `${key}.json`);
513
+ writeFileSync(cacheFile, JSON.stringify({
514
+ config,
515
+ timestamp: Date.now()
516
+ }, null, 2));
517
+ }
518
+ loadConfigCache() {
519
+ if (!existsSync(this.configPaths.cache)) return;
520
+ const files = readdirSync(this.configPaths.cache);
521
+ let loaded = 0;
522
+ files.forEach(file => {
523
+ if (file.endsWith('.json')) {
524
+ try {
525
+ const content = JSON.parse(readFileSync(join(this.configPaths.cache, file), 'utf8'));
526
+ const key = file.replace('.json', '');
527
+ this.configCache.set(key, content);
528
+ loaded++;
529
+ } catch (error) {
530
+ console.warn(`⚠️ Failed to load cache file ${file}`);
531
+ }
532
+ }
533
+ });
534
+ if (loaded > 0) {
535
+ console.log(`📋 Loaded ${loaded} cached configurations`);
536
+ }
537
+ }
538
+ loadConfigTemplates() {
539
+ // Load standard template
540
+ this.templates.set('standard', this.getStandardTemplate());
541
+
542
+ // TODO: Load custom templates from templates directory
543
+ console.log(`📋 Loaded ${this.templates.size} configuration templates`);
544
+ }
545
+ getStandardTemplate() {
546
+ return {
547
+ name: '{CLEAN_DOMAIN_NAME}',
548
+ displayName: '{DISPLAY_NAME}',
549
+ fullDomain: '{DOMAIN_NAME}',
550
+ templateVersion: '1.0',
551
+ generatedAt: '{TIMESTAMP}'
552
+ };
553
+ }
554
+ getCacheKeyHash(token) {
555
+ // Simple hash of token for cache key (first 8 chars)
556
+ return token.substring(0, 8);
557
+ }
558
+ capitalizeFirst(str) {
559
+ return str.charAt(0).toUpperCase() + str.slice(1);
560
+ }
561
+ }
562
+ export default DomainDiscovery;