@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.
- package/CHANGELOG.md +564 -0
- package/LICENSE +21 -0
- package/README.md +1393 -0
- package/bin/README.md +71 -0
- package/bin/clodo-service.js +416 -0
- package/bin/security/security-cli.js +96 -0
- package/bin/service-management/README.md +74 -0
- package/bin/service-management/create-service.js +129 -0
- package/bin/service-management/init-service.js +102 -0
- package/bin/service-management/init-service.js.backup +889 -0
- package/bin/shared/config/customer-cli.js +293 -0
- package/dist/config/ConfigurationManager.js +159 -0
- package/dist/config/CustomerConfigCLI.js +220 -0
- package/dist/config/FeatureManager.js +426 -0
- package/dist/config/customers.js +441 -0
- package/dist/config/domains.js +180 -0
- package/dist/config/features.js +225 -0
- package/dist/config/index.js +6 -0
- package/dist/database/database-orchestrator.js +730 -0
- package/dist/database/index.js +4 -0
- package/dist/deployment/auditor.js +971 -0
- package/dist/deployment/index.js +10 -0
- package/dist/deployment/rollback-manager.js +523 -0
- package/dist/deployment/testers/api-tester.js +80 -0
- package/dist/deployment/testers/auth-tester.js +129 -0
- package/dist/deployment/testers/core.js +217 -0
- package/dist/deployment/testers/database-tester.js +105 -0
- package/dist/deployment/testers/index.js +74 -0
- package/dist/deployment/testers/load-tester.js +120 -0
- package/dist/deployment/testers/performance-tester.js +105 -0
- package/dist/deployment/validator.js +558 -0
- package/dist/deployment/wrangler-deployer.js +574 -0
- package/dist/handlers/GenericRouteHandler.js +532 -0
- package/dist/index.js +39 -0
- package/dist/migration/MigrationAdapters.js +562 -0
- package/dist/modules/ModuleManager.js +668 -0
- package/dist/modules/security.js +98 -0
- package/dist/orchestration/cross-domain-coordinator.js +1083 -0
- package/dist/orchestration/index.js +5 -0
- package/dist/orchestration/modules/DeploymentCoordinator.js +258 -0
- package/dist/orchestration/modules/DomainResolver.js +196 -0
- package/dist/orchestration/modules/StateManager.js +332 -0
- package/dist/orchestration/multi-domain-orchestrator.js +255 -0
- package/dist/routing/EnhancedRouter.js +158 -0
- package/dist/schema/SchemaManager.js +778 -0
- package/dist/security/ConfigurationValidator.js +490 -0
- package/dist/security/DeploymentManager.js +208 -0
- package/dist/security/SecretGenerator.js +142 -0
- package/dist/security/SecurityCLI.js +228 -0
- package/dist/security/index.js +51 -0
- package/dist/security/patterns/environment-rules.js +66 -0
- package/dist/security/patterns/insecure-patterns.js +21 -0
- package/dist/service-management/ConfirmationEngine.js +411 -0
- package/dist/service-management/ErrorTracker.js +294 -0
- package/dist/service-management/GenerationEngine.js +3109 -0
- package/dist/service-management/InputCollector.js +237 -0
- package/dist/service-management/ServiceCreator.js +229 -0
- package/dist/service-management/ServiceInitializer.js +448 -0
- package/dist/service-management/ServiceOrchestrator.js +638 -0
- package/dist/service-management/handlers/ConfigMutator.js +130 -0
- package/dist/service-management/handlers/ConfirmationHandler.js +71 -0
- package/dist/service-management/handlers/GenerationHandler.js +80 -0
- package/dist/service-management/handlers/InputHandler.js +59 -0
- package/dist/service-management/handlers/ValidationHandler.js +203 -0
- package/dist/service-management/index.js +7 -0
- package/dist/services/GenericDataService.js +488 -0
- package/dist/shared/cloudflare/domain-discovery.js +562 -0
- package/dist/shared/cloudflare/domain-manager.js +912 -0
- package/dist/shared/cloudflare/index.js +8 -0
- package/dist/shared/cloudflare/ops.js +387 -0
- package/dist/shared/config/cache.js +1167 -0
- package/dist/shared/config/command-config-manager.js +174 -0
- package/dist/shared/config/customer-cli.js +258 -0
- package/dist/shared/config/index.js +9 -0
- package/dist/shared/config/manager.js +289 -0
- package/dist/shared/database/connection-manager.js +338 -0
- package/dist/shared/database/index.js +7 -0
- package/dist/shared/database/orchestrator.js +632 -0
- package/dist/shared/deployment/auditor.js +971 -0
- package/dist/shared/deployment/index.js +10 -0
- package/dist/shared/deployment/rollback-manager.js +523 -0
- package/dist/shared/deployment/validator.js +558 -0
- package/dist/shared/index.js +32 -0
- package/dist/shared/monitoring/health-checker.js +250 -0
- package/dist/shared/monitoring/index.js +8 -0
- package/dist/shared/monitoring/memory-manager.js +382 -0
- package/dist/shared/monitoring/production-monitor.js +390 -0
- package/dist/shared/production-tester/api-tester.js +80 -0
- package/dist/shared/production-tester/auth-tester.js +129 -0
- package/dist/shared/production-tester/core.js +217 -0
- package/dist/shared/production-tester/database-tester.js +105 -0
- package/dist/shared/production-tester/index.js +74 -0
- package/dist/shared/production-tester/load-tester.js +120 -0
- package/dist/shared/production-tester/performance-tester.js +105 -0
- package/dist/shared/security/api-token-manager.js +296 -0
- package/dist/shared/security/index.js +8 -0
- package/dist/shared/security/secret-generator.js +918 -0
- package/dist/shared/security/secure-token-manager.js +379 -0
- package/dist/shared/utils/error-recovery.js +240 -0
- package/dist/shared/utils/graceful-shutdown-manager.js +380 -0
- package/dist/shared/utils/index.js +9 -0
- package/dist/shared/utils/interactive-prompts.js +134 -0
- package/dist/shared/utils/rate-limiter.js +249 -0
- package/dist/utils/ErrorHandler.js +173 -0
- package/dist/utils/deployment/config-cache.js +1160 -0
- package/dist/utils/deployment/index.js +6 -0
- package/dist/utils/deployment/interactive-prompts.js +97 -0
- package/dist/utils/deployment/secret-generator.js +896 -0
- package/dist/utils/dirname-helper.js +35 -0
- package/dist/utils/domain-config.js +159 -0
- package/dist/utils/error-recovery.js +240 -0
- package/dist/utils/esm-helper.js +52 -0
- package/dist/utils/framework-config.js +481 -0
- package/dist/utils/graceful-shutdown-manager.js +379 -0
- package/dist/utils/health-checker.js +114 -0
- package/dist/utils/index.js +36 -0
- package/dist/utils/prompt-handler.js +98 -0
- package/dist/utils/usage-tracker.js +252 -0
- package/dist/utils/validation.js +112 -0
- package/dist/version/VersionDetector.js +723 -0
- package/dist/worker/index.js +4 -0
- package/dist/worker/integration.js +332 -0
- package/docs/FRAMEWORK-ARCHITECTURE-OVERVIEW.md +206 -0
- package/docs/INTEGRATION_GUIDE.md +2045 -0
- package/docs/README.md +82 -0
- package/docs/SECURITY.md +242 -0
- package/docs/deployment/deployment-guide.md +540 -0
- package/docs/overview.md +280 -0
- package/package.json +176 -0
- package/types/index.d.ts +575 -0
|
@@ -0,0 +1,1160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enterprise Configuration Cache Manager
|
|
3
|
+
*
|
|
4
|
+
* Advanced configuration management system for multi-domain deployments with:
|
|
5
|
+
* - Smart configuration caching with TTL and invalidation
|
|
6
|
+
* - Template-based configuration generation
|
|
7
|
+
* - Runtime configuration discovery and validation
|
|
8
|
+
* - Multi-environment configuration coordination
|
|
9
|
+
* - Configuration versioning and rollback capabilities
|
|
10
|
+
* - Performance-optimized caching with compression
|
|
11
|
+
* - Cross-domain configuration inheritance
|
|
12
|
+
* - Configuration backup and restore
|
|
13
|
+
* - Real-time configuration updates
|
|
14
|
+
* - Compliance and audit trail for config changes
|
|
15
|
+
*
|
|
16
|
+
* @module config-cache
|
|
17
|
+
* @version 2.0.0
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { access, readFile, writeFile, mkdir, readdir, stat } from 'fs/promises';
|
|
21
|
+
import { join, dirname } from 'path';
|
|
22
|
+
import { promisify } from 'util';
|
|
23
|
+
import { exec } from 'child_process';
|
|
24
|
+
import { frameworkConfig } from '../framework-config.js';
|
|
25
|
+
const execAsync = promisify(exec);
|
|
26
|
+
export class ConfigurationCacheManager {
|
|
27
|
+
constructor(options = {}) {
|
|
28
|
+
const timing = frameworkConfig.getTiming();
|
|
29
|
+
const caching = frameworkConfig.getCaching();
|
|
30
|
+
this.config = {
|
|
31
|
+
// Cache configuration from framework config
|
|
32
|
+
cacheDir: options.cacheDir || 'config-cache',
|
|
33
|
+
cacheTTL: options.cacheTTL || timing.cacheTTL,
|
|
34
|
+
maxCacheSize: options.maxCacheSize || caching.maxCacheSize,
|
|
35
|
+
enableCompression: options.enableCompression ?? caching.enableCompression,
|
|
36
|
+
// Template configuration
|
|
37
|
+
templateDir: options.templateDir || 'config-templates',
|
|
38
|
+
enableTemplateInheritance: options.enableTemplateInheritance !== false,
|
|
39
|
+
templateVersioning: options.templateVersioning !== false,
|
|
40
|
+
// Runtime discovery from framework config
|
|
41
|
+
enableRuntimeDiscovery: options.enableRuntimeDiscovery !== false,
|
|
42
|
+
discoveryTimeout: options.discoveryTimeout || timing.discoveryTimeout,
|
|
43
|
+
cloudflareCaching: options.cloudflareCaching !== false,
|
|
44
|
+
// Environments
|
|
45
|
+
environments: options.environments || ['development', 'staging', 'production'],
|
|
46
|
+
defaultEnvironment: options.defaultEnvironment || 'production',
|
|
47
|
+
// Validation and backups
|
|
48
|
+
enableValidation: options.enableValidation !== false,
|
|
49
|
+
enableBackups: options.enableBackups !== false,
|
|
50
|
+
backupRetentionDays: options.backupRetentionDays || 30,
|
|
51
|
+
// Performance
|
|
52
|
+
enableMetrics: options.enableMetrics !== false,
|
|
53
|
+
preloadConfigs: options.preloadConfigs || [],
|
|
54
|
+
// Cross-domain features
|
|
55
|
+
enableCrossDomainSharing: options.enableCrossDomainSharing !== false,
|
|
56
|
+
sharedConfigKeys: options.sharedConfigKeys || ['features', 'settings.security'],
|
|
57
|
+
// Output formats
|
|
58
|
+
outputFormats: options.outputFormats || ['json', 'js', 'yaml', 'env']
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// Cache state
|
|
62
|
+
this.cache = new Map();
|
|
63
|
+
this.templates = new Map();
|
|
64
|
+
this.metrics = {
|
|
65
|
+
cacheHits: 0,
|
|
66
|
+
cacheMisses: 0,
|
|
67
|
+
discoveryRequests: 0,
|
|
68
|
+
validationErrors: 0,
|
|
69
|
+
templateGenerations: 0
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// Configuration registry
|
|
73
|
+
this.configRegistry = {
|
|
74
|
+
domains: new Map(),
|
|
75
|
+
templates: new Map(),
|
|
76
|
+
shared: new Map()
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Built-in configuration templates
|
|
80
|
+
this.builtinTemplates = {
|
|
81
|
+
'domain-standard': this.getStandardDomainTemplate(),
|
|
82
|
+
'domain-minimal': this.getMinimalDomainTemplate(),
|
|
83
|
+
'domain-enterprise': this.getEnterpriseDomainTemplate(),
|
|
84
|
+
'cloudflare-worker': this.getCloudflareWorkerTemplate(),
|
|
85
|
+
'database-standard': this.getStandardDatabaseTemplate()
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// Note: Async initialization required - call initialize() after construction
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Initialize the cache manager asynchronously
|
|
93
|
+
*/
|
|
94
|
+
async initialize() {
|
|
95
|
+
await this.initializeCacheSystem();
|
|
96
|
+
this.loadBuiltinTemplates();
|
|
97
|
+
console.log('🎛️ Configuration Cache Manager initialized');
|
|
98
|
+
if (this.config.enableMetrics) {
|
|
99
|
+
console.log(` 💾 Cache Directory: ${this.config.cacheDir}`);
|
|
100
|
+
console.log(` ⏰ Cache TTL: ${this.config.cacheTTL}ms`);
|
|
101
|
+
console.log(` 📋 Templates: ${Object.keys(this.builtinTemplates).length} builtin`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Initialize cache system directories and structures
|
|
107
|
+
*/
|
|
108
|
+
async initializeCacheSystem() {
|
|
109
|
+
this.paths = {
|
|
110
|
+
cache: this.config.cacheDir,
|
|
111
|
+
templates: this.config.templateDir,
|
|
112
|
+
backups: join(this.config.cacheDir, 'backups'),
|
|
113
|
+
versions: join(this.config.cacheDir, 'versions'),
|
|
114
|
+
shared: join(this.config.cacheDir, 'shared'),
|
|
115
|
+
runtime: join(this.config.cacheDir, 'runtime'),
|
|
116
|
+
metrics: join(this.config.cacheDir, 'metrics')
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// Create directory structure
|
|
120
|
+
for (const path of Object.values(this.paths)) {
|
|
121
|
+
try {
|
|
122
|
+
await access(path);
|
|
123
|
+
} catch {
|
|
124
|
+
await mkdir(path, {
|
|
125
|
+
recursive: true
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Initialize cache metadata
|
|
131
|
+
this.cacheMetadataFile = join(this.paths.cache, 'cache-metadata.json');
|
|
132
|
+
await this.loadCacheMetadata();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Load cache metadata from disk
|
|
137
|
+
*/
|
|
138
|
+
async loadCacheMetadata() {
|
|
139
|
+
try {
|
|
140
|
+
const metadataContent = await readFile(this.cacheMetadataFile, 'utf8');
|
|
141
|
+
const metadata = JSON.parse(metadataContent);
|
|
142
|
+
this.cacheMetadata = {
|
|
143
|
+
...metadata,
|
|
144
|
+
loadedAt: new Date()
|
|
145
|
+
};
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.warn('⚠️ Failed to load cache metadata, initializing new');
|
|
148
|
+
this.cacheMetadata = this.createEmptyMetadata();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Create empty cache metadata structure
|
|
154
|
+
* @returns {Object} Empty metadata
|
|
155
|
+
*/
|
|
156
|
+
createEmptyMetadata() {
|
|
157
|
+
return {
|
|
158
|
+
version: '2.0.0',
|
|
159
|
+
createdAt: new Date(),
|
|
160
|
+
lastUpdate: new Date(),
|
|
161
|
+
cacheEntries: {},
|
|
162
|
+
templateVersions: {},
|
|
163
|
+
sharedConfigs: {}
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Save cache metadata to disk
|
|
169
|
+
*/
|
|
170
|
+
async saveCacheMetadata() {
|
|
171
|
+
this.cacheMetadata.lastUpdate = new Date();
|
|
172
|
+
await writeFile(this.cacheMetadataFile, JSON.stringify(this.cacheMetadata, null, 2));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Load builtin configuration templates
|
|
177
|
+
*/
|
|
178
|
+
loadBuiltinTemplates() {
|
|
179
|
+
Object.entries(this.builtinTemplates).forEach(([name, template]) => {
|
|
180
|
+
this.templates.set(name, {
|
|
181
|
+
...template,
|
|
182
|
+
builtin: true,
|
|
183
|
+
loadedAt: new Date()
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Get or create configuration for a domain
|
|
190
|
+
* @param {string} domain - Domain name
|
|
191
|
+
* @param {Object} options - Configuration options
|
|
192
|
+
* @returns {Promise<Object>} Domain configuration
|
|
193
|
+
*/
|
|
194
|
+
async getOrCreateDomainConfig(domain, options = {}) {
|
|
195
|
+
console.log(`🔍 Getting configuration for domain: ${domain}`);
|
|
196
|
+
const cacheKey = this.generateCacheKey(domain, options.environment || this.config.defaultEnvironment);
|
|
197
|
+
|
|
198
|
+
// Check cache first
|
|
199
|
+
if (this.isCacheValid(cacheKey)) {
|
|
200
|
+
this.metrics.cacheHits++;
|
|
201
|
+
console.log(` 💾 Cache hit for ${domain}`);
|
|
202
|
+
return await this.getCachedConfig(cacheKey);
|
|
203
|
+
}
|
|
204
|
+
this.metrics.cacheMisses++;
|
|
205
|
+
console.log(` 🔄 Cache miss, generating configuration for ${domain}`);
|
|
206
|
+
try {
|
|
207
|
+
let domainConfig;
|
|
208
|
+
|
|
209
|
+
// Check if we have a saved configuration
|
|
210
|
+
const savedConfig = await this.loadSavedConfig(domain, options.environment);
|
|
211
|
+
if (savedConfig && !options.forceRefresh) {
|
|
212
|
+
domainConfig = savedConfig;
|
|
213
|
+
} else if (this.config.enableRuntimeDiscovery && options.cloudflareToken) {
|
|
214
|
+
// Runtime discovery
|
|
215
|
+
domainConfig = await this.discoverDomainConfiguration(domain, options);
|
|
216
|
+
} else {
|
|
217
|
+
// Generate from template
|
|
218
|
+
domainConfig = await this.generateFromTemplate(domain, options);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Validate configuration
|
|
222
|
+
if (this.config.enableValidation) {
|
|
223
|
+
const validation = await this.validateConfiguration(domainConfig);
|
|
224
|
+
if (!validation.valid) {
|
|
225
|
+
throw new Error(`Configuration validation failed: ${validation.errors.join(', ')}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Cache the configuration
|
|
230
|
+
await this.cacheConfiguration(cacheKey, domainConfig);
|
|
231
|
+
|
|
232
|
+
// Save for future use
|
|
233
|
+
await this.saveConfiguration(domain, domainConfig, options.environment);
|
|
234
|
+
return domainConfig;
|
|
235
|
+
} catch (error) {
|
|
236
|
+
console.error(`❌ Failed to get configuration for ${domain}: ${error.message}`);
|
|
237
|
+
throw error;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Discover domain configuration from Cloudflare API
|
|
243
|
+
* @param {string} domain - Domain name
|
|
244
|
+
* @param {Object} options - Discovery options
|
|
245
|
+
* @returns {Promise<Object>} Discovered configuration
|
|
246
|
+
*/
|
|
247
|
+
async discoverDomainConfiguration(domain, options = {}) {
|
|
248
|
+
console.log(` 🔍 Discovering Cloudflare configuration for ${domain}`);
|
|
249
|
+
this.metrics.discoveryRequests++;
|
|
250
|
+
const {
|
|
251
|
+
cloudflareToken,
|
|
252
|
+
environment = this.config.defaultEnvironment
|
|
253
|
+
} = options;
|
|
254
|
+
if (!cloudflareToken) {
|
|
255
|
+
throw new Error('Cloudflare token required for runtime discovery');
|
|
256
|
+
}
|
|
257
|
+
try {
|
|
258
|
+
// Get Cloudflare account information
|
|
259
|
+
const accountInfo = await this.fetchCloudflareAccounts(cloudflareToken);
|
|
260
|
+
const zoneInfo = await this.fetchCloudflareZone(domain, cloudflareToken);
|
|
261
|
+
|
|
262
|
+
// Build configuration from discovered data
|
|
263
|
+
const discoveredConfig = {
|
|
264
|
+
domain,
|
|
265
|
+
environment,
|
|
266
|
+
// Cloudflare metadata
|
|
267
|
+
cloudflare: {
|
|
268
|
+
accountId: accountInfo.id,
|
|
269
|
+
accountName: accountInfo.name,
|
|
270
|
+
zoneId: zoneInfo.id,
|
|
271
|
+
zoneName: zoneInfo.name,
|
|
272
|
+
discoveredAt: new Date().toISOString()
|
|
273
|
+
},
|
|
274
|
+
// Generate standard configuration structure
|
|
275
|
+
...this.generateStandardConfig(domain, accountInfo, zoneInfo),
|
|
276
|
+
// Mark as dynamically discovered
|
|
277
|
+
metadata: {
|
|
278
|
+
type: 'discovered',
|
|
279
|
+
source: 'cloudflare-api',
|
|
280
|
+
timestamp: new Date(),
|
|
281
|
+
version: '1.0.0'
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
console.log(` ✅ Successfully discovered configuration for ${domain}`);
|
|
285
|
+
return discoveredConfig;
|
|
286
|
+
} catch (error) {
|
|
287
|
+
console.error(` ❌ Discovery failed for ${domain}: ${error.message}`);
|
|
288
|
+
throw error;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Generate configuration from template
|
|
294
|
+
* @param {string} domain - Domain name
|
|
295
|
+
* @param {Object} options - Generation options
|
|
296
|
+
* @returns {Promise<Object>} Generated configuration
|
|
297
|
+
*/
|
|
298
|
+
async generateFromTemplate(domain, options = {}) {
|
|
299
|
+
console.log(` 📋 Generating configuration from template for ${domain}`);
|
|
300
|
+
this.metrics.templateGenerations++;
|
|
301
|
+
const {
|
|
302
|
+
templateName = 'domain-standard',
|
|
303
|
+
environment = this.config.defaultEnvironment,
|
|
304
|
+
customValues = {}
|
|
305
|
+
} = options;
|
|
306
|
+
|
|
307
|
+
// Get template
|
|
308
|
+
const template = this.getTemplate(templateName);
|
|
309
|
+
if (!template) {
|
|
310
|
+
throw new Error(`Template not found: ${templateName}`);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Process template with domain-specific values
|
|
314
|
+
const templateValues = {
|
|
315
|
+
domain,
|
|
316
|
+
environment,
|
|
317
|
+
domainKey: this.getDomainKey(domain),
|
|
318
|
+
displayName: this.getDisplayName(domain),
|
|
319
|
+
timestamp: new Date().toISOString(),
|
|
320
|
+
...customValues
|
|
321
|
+
};
|
|
322
|
+
const generatedConfig = await this.processTemplate(template, templateValues);
|
|
323
|
+
|
|
324
|
+
// Apply cross-domain sharing if enabled
|
|
325
|
+
if (this.config.enableCrossDomainSharing) {
|
|
326
|
+
await this.applyCrossDomainSharing(generatedConfig);
|
|
327
|
+
}
|
|
328
|
+
generatedConfig.metadata = {
|
|
329
|
+
type: 'generated',
|
|
330
|
+
source: `template:${templateName}`,
|
|
331
|
+
timestamp: new Date(),
|
|
332
|
+
version: template.version || '1.0.0'
|
|
333
|
+
};
|
|
334
|
+
console.log(` ✅ Generated configuration from template: ${templateName}`);
|
|
335
|
+
return generatedConfig;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Process template with values
|
|
340
|
+
* @param {Object} template - Template object
|
|
341
|
+
* @param {Object} values - Template values
|
|
342
|
+
* @returns {Promise<Object>} Processed configuration
|
|
343
|
+
*/
|
|
344
|
+
async processTemplate(template, values) {
|
|
345
|
+
// Deep clone template structure
|
|
346
|
+
const config = JSON.parse(JSON.stringify(template.structure || template));
|
|
347
|
+
|
|
348
|
+
// Process template variables
|
|
349
|
+
const processValue = obj => {
|
|
350
|
+
if (typeof obj === 'string') {
|
|
351
|
+
return obj.replace(/\{\{(\w+)\}\}/g, (match, key) => {
|
|
352
|
+
return values[key] || match;
|
|
353
|
+
});
|
|
354
|
+
} else if (Array.isArray(obj)) {
|
|
355
|
+
return obj.map(processValue);
|
|
356
|
+
} else if (obj && typeof obj === 'object') {
|
|
357
|
+
const processed = {};
|
|
358
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
359
|
+
processed[key] = processValue(value);
|
|
360
|
+
}
|
|
361
|
+
return processed;
|
|
362
|
+
}
|
|
363
|
+
return obj;
|
|
364
|
+
};
|
|
365
|
+
return processValue(config);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Apply cross-domain configuration sharing
|
|
370
|
+
* @param {Object} config - Configuration to enhance
|
|
371
|
+
*/
|
|
372
|
+
async applyCrossDomainSharing(config) {
|
|
373
|
+
const sharedConfigs = await this.getSharedConfigurations();
|
|
374
|
+
for (const sharedKey of this.config.sharedConfigKeys) {
|
|
375
|
+
if (sharedConfigs[sharedKey]) {
|
|
376
|
+
this.setNestedValue(config, sharedKey, sharedConfigs[sharedKey]);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Get shared configurations
|
|
383
|
+
* @returns {Promise<Object>} Shared configurations
|
|
384
|
+
*/
|
|
385
|
+
async getSharedConfigurations() {
|
|
386
|
+
const sharedFile = join(this.paths.shared, 'shared-configs.json');
|
|
387
|
+
try {
|
|
388
|
+
const sharedContent = await readFile(sharedFile, 'utf8');
|
|
389
|
+
return JSON.parse(sharedContent);
|
|
390
|
+
} catch (error) {
|
|
391
|
+
console.warn('⚠️ Failed to load shared configurations');
|
|
392
|
+
return {};
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Validate configuration object
|
|
398
|
+
* @param {Object} config - Configuration to validate
|
|
399
|
+
* @returns {Promise<Object>} Validation result
|
|
400
|
+
*/
|
|
401
|
+
async validateConfiguration(config) {
|
|
402
|
+
const errors = [];
|
|
403
|
+
const warnings = [];
|
|
404
|
+
|
|
405
|
+
// Basic structure validation
|
|
406
|
+
if (!config.domain) errors.push('Missing domain');
|
|
407
|
+
if (!config.environment) errors.push('Missing environment');
|
|
408
|
+
|
|
409
|
+
// Cloudflare validation
|
|
410
|
+
if (config.cloudflare) {
|
|
411
|
+
if (!config.cloudflare.accountId) errors.push('Missing Cloudflare account ID');
|
|
412
|
+
if (!config.cloudflare.zoneId) errors.push('Missing Cloudflare zone ID');
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Services validation
|
|
416
|
+
if (config.services) {
|
|
417
|
+
for (const [serviceName, serviceConfig] of Object.entries(config.services)) {
|
|
418
|
+
if (!serviceConfig[config.environment]) {
|
|
419
|
+
warnings.push(`Missing ${serviceName} configuration for ${config.environment}`);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Database validation
|
|
425
|
+
if (config.databases && config.databases[config.environment]) {
|
|
426
|
+
const dbConfig = config.databases[config.environment];
|
|
427
|
+
if (!dbConfig.name) errors.push(`Missing database name for ${config.environment}`);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// CORS validation
|
|
431
|
+
if (config.corsOrigins && config.corsOrigins[config.environment]) {
|
|
432
|
+
const corsOrigins = config.corsOrigins[config.environment];
|
|
433
|
+
if (!Array.isArray(corsOrigins) || corsOrigins.length === 0) {
|
|
434
|
+
warnings.push(`No CORS origins defined for ${config.environment}`);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
if (errors.length > 0) {
|
|
438
|
+
this.metrics.validationErrors++;
|
|
439
|
+
}
|
|
440
|
+
return {
|
|
441
|
+
valid: errors.length === 0,
|
|
442
|
+
errors,
|
|
443
|
+
warnings
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Cache configuration with TTL
|
|
449
|
+
* @param {string} cacheKey - Cache key
|
|
450
|
+
* @param {Object} config - Configuration to cache
|
|
451
|
+
*/
|
|
452
|
+
async cacheConfiguration(cacheKey, config) {
|
|
453
|
+
const cacheEntry = {
|
|
454
|
+
config,
|
|
455
|
+
cachedAt: new Date(),
|
|
456
|
+
expiresAt: new Date(Date.now() + this.config.cacheTTL),
|
|
457
|
+
size: JSON.stringify(config).length
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
// Compress if enabled
|
|
461
|
+
if (this.config.enableCompression && cacheEntry.size > 1024) {
|
|
462
|
+
// In a real implementation, you'd use a compression library
|
|
463
|
+
cacheEntry.compressed = true;
|
|
464
|
+
cacheEntry.originalSize = cacheEntry.size;
|
|
465
|
+
}
|
|
466
|
+
this.cache.set(cacheKey, cacheEntry);
|
|
467
|
+
|
|
468
|
+
// Update metadata
|
|
469
|
+
this.cacheMetadata.cacheEntries[cacheKey] = {
|
|
470
|
+
cachedAt: cacheEntry.cachedAt,
|
|
471
|
+
expiresAt: cacheEntry.expiresAt,
|
|
472
|
+
size: cacheEntry.size
|
|
473
|
+
};
|
|
474
|
+
this.saveCacheMetadata();
|
|
475
|
+
|
|
476
|
+
// Write to disk cache
|
|
477
|
+
await this.writeToDiskCache(cacheKey, cacheEntry);
|
|
478
|
+
console.log(` 💾 Cached configuration: ${cacheKey}`);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Check if cache entry is valid
|
|
483
|
+
* @param {string} cacheKey - Cache key
|
|
484
|
+
* @returns {boolean} True if cache is valid
|
|
485
|
+
*/
|
|
486
|
+
isCacheValid(cacheKey) {
|
|
487
|
+
const cacheEntry = this.cache.get(cacheKey);
|
|
488
|
+
if (!cacheEntry) return false;
|
|
489
|
+
return new Date() < cacheEntry.expiresAt;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Get cached configuration
|
|
494
|
+
* @param {string} cacheKey - Cache key
|
|
495
|
+
* @returns {Promise<Object>} Cached configuration
|
|
496
|
+
*/
|
|
497
|
+
async getCachedConfig(cacheKey) {
|
|
498
|
+
const cacheEntry = this.cache.get(cacheKey);
|
|
499
|
+
if (cacheEntry) {
|
|
500
|
+
return cacheEntry.config;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// Try to load from disk cache
|
|
504
|
+
return await this.loadFromDiskCache(cacheKey);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Write cache entry to disk
|
|
509
|
+
* @param {string} cacheKey - Cache key
|
|
510
|
+
* @param {Object} cacheEntry - Cache entry
|
|
511
|
+
*/
|
|
512
|
+
async writeToDiskCache(cacheKey, cacheEntry) {
|
|
513
|
+
try {
|
|
514
|
+
const cacheFile = join(this.paths.cache, `${cacheKey}.json`);
|
|
515
|
+
await writeFile(cacheFile, JSON.stringify(cacheEntry, null, 2));
|
|
516
|
+
} catch (error) {
|
|
517
|
+
console.warn(`⚠️ Failed to write disk cache: ${error.message}`);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Load configuration from disk cache
|
|
523
|
+
* @param {string} cacheKey - Cache key
|
|
524
|
+
* @returns {Promise<Object|null>} Cached configuration or null
|
|
525
|
+
*/
|
|
526
|
+
async loadFromDiskCache(cacheKey) {
|
|
527
|
+
try {
|
|
528
|
+
const cacheFile = join(this.paths.cache, `${cacheKey}.json`);
|
|
529
|
+
try {
|
|
530
|
+
const cacheContent = await readFile(cacheFile, 'utf8');
|
|
531
|
+
const cacheEntry = JSON.parse(cacheContent);
|
|
532
|
+
|
|
533
|
+
// Check expiration
|
|
534
|
+
if (new Date() < new Date(cacheEntry.expiresAt)) {
|
|
535
|
+
this.cache.set(cacheKey, cacheEntry);
|
|
536
|
+
return cacheEntry.config;
|
|
537
|
+
}
|
|
538
|
+
} catch {
|
|
539
|
+
// File doesn't exist or can't be read
|
|
540
|
+
}
|
|
541
|
+
} catch (error) {
|
|
542
|
+
console.warn(`⚠️ Failed to load disk cache: ${error.message}`);
|
|
543
|
+
}
|
|
544
|
+
return null;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Save configuration to persistent storage
|
|
549
|
+
* @param {string} domain - Domain name
|
|
550
|
+
* @param {Object} config - Configuration to save
|
|
551
|
+
* @param {string} environment - Environment
|
|
552
|
+
*/
|
|
553
|
+
async saveConfiguration(domain, config, environment) {
|
|
554
|
+
const configDir = join(this.paths.cache, 'domains', domain);
|
|
555
|
+
try {
|
|
556
|
+
await access(configDir);
|
|
557
|
+
} catch {
|
|
558
|
+
await mkdir(configDir, {
|
|
559
|
+
recursive: true
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
const configFile = join(configDir, `${environment || 'default'}.json`);
|
|
563
|
+
|
|
564
|
+
// Create backup if file exists
|
|
565
|
+
try {
|
|
566
|
+
await access(configFile);
|
|
567
|
+
if (this.config.enableBackups) {
|
|
568
|
+
await this.createConfigBackup(configFile);
|
|
569
|
+
}
|
|
570
|
+
} catch {
|
|
571
|
+
// File doesn't exist, no backup needed
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// Save configuration with metadata
|
|
575
|
+
const saveData = {
|
|
576
|
+
...config,
|
|
577
|
+
savedAt: new Date(),
|
|
578
|
+
version: config.version || '1.0.0'
|
|
579
|
+
};
|
|
580
|
+
await writeFile(configFile, JSON.stringify(saveData, null, 2));
|
|
581
|
+
|
|
582
|
+
// Generate additional formats if requested
|
|
583
|
+
await this.generateOutputFormats(domain, config, environment);
|
|
584
|
+
console.log(` 💾 Saved configuration: ${domain}/${environment}`);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Load saved configuration
|
|
589
|
+
* @param {string} domain - Domain name
|
|
590
|
+
* @param {string} environment - Environment
|
|
591
|
+
* @returns {Promise<Object|null>} Saved configuration or null
|
|
592
|
+
*/
|
|
593
|
+
async loadSavedConfig(domain, environment) {
|
|
594
|
+
const configFile = join(this.paths.cache, 'domains', domain, `${environment || 'default'}.json`);
|
|
595
|
+
try {
|
|
596
|
+
await access(configFile);
|
|
597
|
+
const content = await readFile(configFile, 'utf8');
|
|
598
|
+
const config = JSON.parse(content);
|
|
599
|
+
|
|
600
|
+
// Check if config is still valid (not too old)
|
|
601
|
+
const savedAt = new Date(config.savedAt);
|
|
602
|
+
const maxAge = 24 * 60 * 60 * 1000; // 24 hours
|
|
603
|
+
|
|
604
|
+
if (Date.now() - savedAt.getTime() < maxAge) {
|
|
605
|
+
return config;
|
|
606
|
+
}
|
|
607
|
+
} catch (error) {
|
|
608
|
+
if (error.code !== 'ENOENT') {
|
|
609
|
+
console.warn(`⚠️ Failed to load saved config for ${domain}: ${error.message}`);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
return null;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* Generate output formats for configuration
|
|
617
|
+
* @param {string} domain - Domain name
|
|
618
|
+
* @param {Object} config - Configuration
|
|
619
|
+
* @param {string} environment - Environment
|
|
620
|
+
*/
|
|
621
|
+
async generateOutputFormats(domain, config, environment) {
|
|
622
|
+
if (!this.config.outputFormats.length) return;
|
|
623
|
+
const outputDir = join(this.paths.cache, 'output', domain);
|
|
624
|
+
try {
|
|
625
|
+
await access(outputDir);
|
|
626
|
+
} catch {
|
|
627
|
+
await mkdir(outputDir, {
|
|
628
|
+
recursive: true
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
for (const format of this.config.outputFormats) {
|
|
632
|
+
try {
|
|
633
|
+
const fileName = `${environment || 'default'}.${format}`;
|
|
634
|
+
const filePath = join(outputDir, fileName);
|
|
635
|
+
let content = '';
|
|
636
|
+
switch (format) {
|
|
637
|
+
case 'json':
|
|
638
|
+
content = JSON.stringify(config, null, 2);
|
|
639
|
+
break;
|
|
640
|
+
case 'js':
|
|
641
|
+
content = `// Auto-generated configuration for ${domain}
|
|
642
|
+
export const config = ${JSON.stringify(config, null, 2)};
|
|
643
|
+
export default config;`;
|
|
644
|
+
break;
|
|
645
|
+
case 'yaml':
|
|
646
|
+
content = this.convertToYaml(config);
|
|
647
|
+
break;
|
|
648
|
+
case 'env':
|
|
649
|
+
content = this.convertToEnv(config);
|
|
650
|
+
break;
|
|
651
|
+
default:
|
|
652
|
+
console.warn(`⚠️ Unknown output format: ${format}`);
|
|
653
|
+
continue;
|
|
654
|
+
}
|
|
655
|
+
await writeFile(filePath, content);
|
|
656
|
+
console.log(` 📄 Generated ${format.toUpperCase()}: ${fileName}`);
|
|
657
|
+
} catch (error) {
|
|
658
|
+
console.error(`❌ Failed to generate ${format} format: ${error.message}`);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* Convert configuration to YAML format
|
|
665
|
+
* @param {Object} config - Configuration object
|
|
666
|
+
* @returns {string} YAML string
|
|
667
|
+
*/
|
|
668
|
+
convertToYaml(config) {
|
|
669
|
+
// Simple YAML conversion - in production, use a proper YAML library
|
|
670
|
+
const yamlify = (obj, indent = 0) => {
|
|
671
|
+
const spaces = ' '.repeat(indent);
|
|
672
|
+
let yaml = '';
|
|
673
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
674
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
675
|
+
yaml += `${spaces}${key}:\n`;
|
|
676
|
+
yaml += yamlify(value, indent + 1);
|
|
677
|
+
} else if (Array.isArray(value)) {
|
|
678
|
+
yaml += `${spaces}${key}:\n`;
|
|
679
|
+
value.forEach(item => {
|
|
680
|
+
yaml += `${spaces} - ${JSON.stringify(item)}\n`;
|
|
681
|
+
});
|
|
682
|
+
} else {
|
|
683
|
+
yaml += `${spaces}${key}: ${JSON.stringify(value)}\n`;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
return yaml;
|
|
687
|
+
};
|
|
688
|
+
return `# Auto-generated configuration\n${yamlify(config)}`;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/**
|
|
692
|
+
* Convert configuration to environment variables
|
|
693
|
+
* @param {Object} config - Configuration object
|
|
694
|
+
* @returns {string} Environment variables string
|
|
695
|
+
*/
|
|
696
|
+
convertToEnv(config) {
|
|
697
|
+
const envVars = [];
|
|
698
|
+
const flatten = (obj, prefix = '') => {
|
|
699
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
700
|
+
const envKey = (prefix + key).toUpperCase().replace(/[^A-Z0-9]/g, '_');
|
|
701
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
702
|
+
flatten(value, `${prefix}${key}_`);
|
|
703
|
+
} else if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
704
|
+
envVars.push(`${envKey}=${value}`);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
};
|
|
708
|
+
flatten(config);
|
|
709
|
+
return envVars.join('\n');
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* Create backup of configuration file
|
|
714
|
+
* @param {string} configFile - Configuration file path
|
|
715
|
+
*/
|
|
716
|
+
async createConfigBackup(configFile) {
|
|
717
|
+
try {
|
|
718
|
+
const backupDir = join(this.paths.backups, new Date().toISOString().split('T')[0]);
|
|
719
|
+
try {
|
|
720
|
+
await access(backupDir);
|
|
721
|
+
} catch {
|
|
722
|
+
await mkdir(backupDir, {
|
|
723
|
+
recursive: true
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
const backupFile = join(backupDir, `${Date.now()}-${require('path').basename(configFile)}`);
|
|
727
|
+
const content = await readFile(configFile, 'utf8');
|
|
728
|
+
await writeFile(backupFile, content);
|
|
729
|
+
console.log(` 💾 Created backup: ${backupFile}`);
|
|
730
|
+
} catch (error) {
|
|
731
|
+
console.warn(`⚠️ Failed to create backup: ${error.message}`);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
/**
|
|
736
|
+
* Invalidate cache for domain
|
|
737
|
+
* @param {string} domain - Domain name
|
|
738
|
+
* @param {string} environment - Environment (optional)
|
|
739
|
+
*/
|
|
740
|
+
invalidateCache(domain, environment = null) {
|
|
741
|
+
const pattern = environment ? this.generateCacheKey(domain, environment) : domain;
|
|
742
|
+
let invalidated = 0;
|
|
743
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
744
|
+
if (key.includes(pattern)) {
|
|
745
|
+
this.cache.delete(key);
|
|
746
|
+
delete this.cacheMetadata.cacheEntries[key];
|
|
747
|
+
invalidated++;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
this.saveCacheMetadata();
|
|
751
|
+
console.log(`🗑️ Invalidated ${invalidated} cache entries for ${domain}${environment ? `/${environment}` : ''}`);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
* Get cache statistics
|
|
756
|
+
* @returns {Object} Cache statistics
|
|
757
|
+
*/
|
|
758
|
+
getCacheStatistics() {
|
|
759
|
+
const stats = {
|
|
760
|
+
entries: this.cache.size,
|
|
761
|
+
hitRate: this.metrics.cacheHits / (this.metrics.cacheHits + this.metrics.cacheMisses) * 100,
|
|
762
|
+
metrics: {
|
|
763
|
+
...this.metrics
|
|
764
|
+
},
|
|
765
|
+
memory: {
|
|
766
|
+
cacheSize: 0,
|
|
767
|
+
totalEntries: this.cache.size
|
|
768
|
+
}
|
|
769
|
+
};
|
|
770
|
+
|
|
771
|
+
// Calculate memory usage
|
|
772
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
773
|
+
stats.memory.cacheSize += entry.size || 0;
|
|
774
|
+
}
|
|
775
|
+
return stats;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
/**
|
|
779
|
+
* Generate cache key
|
|
780
|
+
* @param {string} domain - Domain name
|
|
781
|
+
* @param {string} environment - Environment
|
|
782
|
+
* @returns {string} Cache key
|
|
783
|
+
*/
|
|
784
|
+
generateCacheKey(domain, environment) {
|
|
785
|
+
return `${domain}:${environment}`;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/**
|
|
789
|
+
* Get domain key (sanitized version)
|
|
790
|
+
* @param {string} domain - Domain name
|
|
791
|
+
* @returns {string} Domain key
|
|
792
|
+
*/
|
|
793
|
+
getDomainKey(domain) {
|
|
794
|
+
return domain.replace(/\./g, '').replace(/[^a-zA-Z0-9]/g, '');
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/**
|
|
798
|
+
* Get display name for domain
|
|
799
|
+
* @param {string} domain - Domain name
|
|
800
|
+
* @returns {string} Display name
|
|
801
|
+
*/
|
|
802
|
+
getDisplayName(domain) {
|
|
803
|
+
return domain.split('.')[0].split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ');
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
/**
|
|
807
|
+
* Get template by name
|
|
808
|
+
* @param {string} templateName - Template name
|
|
809
|
+
* @returns {Object|null} Template object
|
|
810
|
+
*/
|
|
811
|
+
getTemplate(templateName) {
|
|
812
|
+
return this.templates.get(templateName);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
/**
|
|
816
|
+
* Set nested value in object using dot notation
|
|
817
|
+
* @param {Object} obj - Target object
|
|
818
|
+
* @param {string} path - Path (e.g., 'a.b.c')
|
|
819
|
+
* @param {*} value - Value to set
|
|
820
|
+
*/
|
|
821
|
+
setNestedValue(obj, path, value) {
|
|
822
|
+
const keys = path.split('.');
|
|
823
|
+
let current = obj;
|
|
824
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
825
|
+
const key = keys[i];
|
|
826
|
+
if (!(key in current) || typeof current[key] !== 'object') {
|
|
827
|
+
current[key] = {};
|
|
828
|
+
}
|
|
829
|
+
current = current[key];
|
|
830
|
+
}
|
|
831
|
+
current[keys[keys.length - 1]] = value;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
// Built-in template definitions
|
|
835
|
+
|
|
836
|
+
/**
|
|
837
|
+
* Get standard domain template
|
|
838
|
+
* @returns {Object} Standard template
|
|
839
|
+
*/
|
|
840
|
+
getStandardDomainTemplate() {
|
|
841
|
+
return {
|
|
842
|
+
version: '1.0.0',
|
|
843
|
+
name: 'domain-standard',
|
|
844
|
+
description: 'Standard domain configuration template',
|
|
845
|
+
structure: {
|
|
846
|
+
domain: '{{domain}}',
|
|
847
|
+
environment: '{{environment}}',
|
|
848
|
+
name: '{{domainKey}}',
|
|
849
|
+
displayName: '{{displayName}}',
|
|
850
|
+
domains: {
|
|
851
|
+
production: '{{domain}}',
|
|
852
|
+
staging: '{{domain}}',
|
|
853
|
+
development: '{{domain}}'
|
|
854
|
+
},
|
|
855
|
+
services: {
|
|
856
|
+
frontend: {
|
|
857
|
+
production: 'https://{{domain}}',
|
|
858
|
+
staging: 'https://staging.{{domain}}',
|
|
859
|
+
development: 'http://localhost:3000'
|
|
860
|
+
},
|
|
861
|
+
api: {
|
|
862
|
+
production: 'https://{{domainKey}}-data-service.tamylatrading.workers.dev',
|
|
863
|
+
staging: 'https://{{domainKey}}-data-service-staging.tamylatrading.workers.dev',
|
|
864
|
+
development: 'http://localhost:8787'
|
|
865
|
+
},
|
|
866
|
+
auth: {
|
|
867
|
+
production: 'https://auth.{{domain}}',
|
|
868
|
+
staging: 'https://auth-staging.{{domain}}',
|
|
869
|
+
development: 'http://localhost:8787'
|
|
870
|
+
}
|
|
871
|
+
},
|
|
872
|
+
corsOrigins: {
|
|
873
|
+
production: ['https://{{domain}}', 'https://*.{{domain}}'],
|
|
874
|
+
staging: ['https://staging.{{domain}}', 'https://*.staging.{{domain}}'],
|
|
875
|
+
development: ['http://localhost:3000', 'http://localhost:8787']
|
|
876
|
+
},
|
|
877
|
+
databases: {
|
|
878
|
+
production: {
|
|
879
|
+
name: '{{domainKey}}-auth-db',
|
|
880
|
+
id: null
|
|
881
|
+
},
|
|
882
|
+
staging: {
|
|
883
|
+
name: '{{domainKey}}-auth-db-staging',
|
|
884
|
+
id: null
|
|
885
|
+
},
|
|
886
|
+
development: {
|
|
887
|
+
name: '{{domainKey}}-auth-db-local',
|
|
888
|
+
id: null
|
|
889
|
+
}
|
|
890
|
+
},
|
|
891
|
+
features: {
|
|
892
|
+
magicLinkAuth: true,
|
|
893
|
+
fileStorage: true,
|
|
894
|
+
userProfiles: true,
|
|
895
|
+
logging: true,
|
|
896
|
+
webhooks: false
|
|
897
|
+
},
|
|
898
|
+
settings: {
|
|
899
|
+
magicLinkExpiryMinutes: 15,
|
|
900
|
+
rateLimitWindowMs: 900000,
|
|
901
|
+
rateLimitMax: 100,
|
|
902
|
+
maxFileSize: 26214400,
|
|
903
|
+
allowedFileTypes: ['image/jpeg', 'image/png', 'image/webp', 'image/gif', 'application/pdf', 'video/mp4', 'video/webm', 'audio/mpeg', 'audio/wav']
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
/**
|
|
910
|
+
* Get minimal domain template
|
|
911
|
+
* @returns {Object} Minimal template
|
|
912
|
+
*/
|
|
913
|
+
getMinimalDomainTemplate() {
|
|
914
|
+
return {
|
|
915
|
+
version: '1.0.0',
|
|
916
|
+
name: 'domain-minimal',
|
|
917
|
+
description: 'Minimal domain configuration template',
|
|
918
|
+
structure: {
|
|
919
|
+
domain: '{{domain}}',
|
|
920
|
+
environment: '{{environment}}',
|
|
921
|
+
name: '{{domainKey}}',
|
|
922
|
+
services: {
|
|
923
|
+
api: {
|
|
924
|
+
production: 'https://{{domainKey}}-data-service.tamylatrading.workers.dev'
|
|
925
|
+
}
|
|
926
|
+
},
|
|
927
|
+
features: {
|
|
928
|
+
magicLinkAuth: true,
|
|
929
|
+
logging: false
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
/**
|
|
936
|
+
* Get enterprise domain template
|
|
937
|
+
* @returns {Object} Enterprise template
|
|
938
|
+
*/
|
|
939
|
+
getEnterpriseDomainTemplate() {
|
|
940
|
+
return {
|
|
941
|
+
version: '1.0.0',
|
|
942
|
+
name: 'domain-enterprise',
|
|
943
|
+
description: 'Enterprise domain configuration template',
|
|
944
|
+
structure: {
|
|
945
|
+
domain: '{{domain}}',
|
|
946
|
+
environment: '{{environment}}',
|
|
947
|
+
name: '{{domainKey}}',
|
|
948
|
+
displayName: '{{displayName}}',
|
|
949
|
+
domains: {
|
|
950
|
+
production: '{{domain}}',
|
|
951
|
+
staging: 'staging.{{domain}}',
|
|
952
|
+
development: 'dev.{{domain}}'
|
|
953
|
+
},
|
|
954
|
+
services: {
|
|
955
|
+
frontend: {
|
|
956
|
+
production: 'https://{{domain}}',
|
|
957
|
+
staging: 'https://staging.{{domain}}',
|
|
958
|
+
development: 'https://dev.{{domain}}'
|
|
959
|
+
},
|
|
960
|
+
api: {
|
|
961
|
+
production: 'https://api.{{domain}}',
|
|
962
|
+
staging: 'https://api-staging.{{domain}}',
|
|
963
|
+
development: 'https://api-dev.{{domain}}'
|
|
964
|
+
},
|
|
965
|
+
cdn: {
|
|
966
|
+
production: 'https://cdn.{{domain}}',
|
|
967
|
+
staging: 'https://cdn-staging.{{domain}}',
|
|
968
|
+
development: 'https://cdn-dev.{{domain}}'
|
|
969
|
+
}
|
|
970
|
+
},
|
|
971
|
+
features: {
|
|
972
|
+
magicLinkAuth: true,
|
|
973
|
+
fileStorage: true,
|
|
974
|
+
userProfiles: true,
|
|
975
|
+
logging: true,
|
|
976
|
+
webhooks: true,
|
|
977
|
+
analytics: true,
|
|
978
|
+
monitoring: true,
|
|
979
|
+
backup: true
|
|
980
|
+
},
|
|
981
|
+
security: {
|
|
982
|
+
enableCSP: true,
|
|
983
|
+
enableHSTS: true,
|
|
984
|
+
requireHTTPS: true,
|
|
985
|
+
rateLimiting: true
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
/**
|
|
992
|
+
* Get Cloudflare Worker template
|
|
993
|
+
* @returns {Object} Worker template
|
|
994
|
+
*/
|
|
995
|
+
getCloudflareWorkerTemplate() {
|
|
996
|
+
return {
|
|
997
|
+
version: '1.0.0',
|
|
998
|
+
name: 'cloudflare-worker',
|
|
999
|
+
description: 'Cloudflare Worker configuration template',
|
|
1000
|
+
structure: {
|
|
1001
|
+
name: '{{domainKey}}-data-service',
|
|
1002
|
+
main: 'src/worker/worker.js',
|
|
1003
|
+
compatibility_date: '2023-12-01',
|
|
1004
|
+
env: {
|
|
1005
|
+
production: {
|
|
1006
|
+
vars: {
|
|
1007
|
+
ENVIRONMENT: 'production',
|
|
1008
|
+
CORS_ORIGIN: 'https://{{domain}}'
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
/**
|
|
1017
|
+
* Get standard database template
|
|
1018
|
+
* @returns {Object} Database template
|
|
1019
|
+
*/
|
|
1020
|
+
getStandardDatabaseTemplate() {
|
|
1021
|
+
return {
|
|
1022
|
+
version: '1.0.0',
|
|
1023
|
+
name: 'database-standard',
|
|
1024
|
+
description: 'Standard database configuration template',
|
|
1025
|
+
structure: {
|
|
1026
|
+
databases: [{
|
|
1027
|
+
binding: 'DB',
|
|
1028
|
+
database_name: '{{domainKey}}-auth-db',
|
|
1029
|
+
database_id: null
|
|
1030
|
+
}],
|
|
1031
|
+
migrations_table: 'migrations',
|
|
1032
|
+
backup_schedule: 'daily',
|
|
1033
|
+
retention_days: 30
|
|
1034
|
+
}
|
|
1035
|
+
};
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
/**
|
|
1039
|
+
* Fetch Cloudflare accounts
|
|
1040
|
+
* @param {string} token - Cloudflare API token
|
|
1041
|
+
* @returns {Promise<Object>} Account information
|
|
1042
|
+
*/
|
|
1043
|
+
async fetchCloudflareAccounts(token) {
|
|
1044
|
+
// Implementation would make actual API call
|
|
1045
|
+
// This is a placeholder for the real implementation
|
|
1046
|
+
return {
|
|
1047
|
+
id: 'account-id',
|
|
1048
|
+
name: 'Account Name'
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
/**
|
|
1053
|
+
* Fetch Cloudflare zone information
|
|
1054
|
+
* @param {string} domain - Domain name
|
|
1055
|
+
* @param {string} token - Cloudflare API token
|
|
1056
|
+
* @returns {Promise<Object>} Zone information
|
|
1057
|
+
*/
|
|
1058
|
+
async fetchCloudflareZone(domain, token) {
|
|
1059
|
+
// Implementation would make actual API call
|
|
1060
|
+
// This is a placeholder for the real implementation
|
|
1061
|
+
return {
|
|
1062
|
+
id: 'zone-id',
|
|
1063
|
+
name: domain
|
|
1064
|
+
};
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
/**
|
|
1068
|
+
* Generate standard configuration structure
|
|
1069
|
+
* @param {string} domain - Domain name
|
|
1070
|
+
* @param {Object} accountInfo - Account information
|
|
1071
|
+
* @param {Object} zoneInfo - Zone information
|
|
1072
|
+
* @returns {Object} Standard configuration
|
|
1073
|
+
*/
|
|
1074
|
+
generateStandardConfig(domain, accountInfo, zoneInfo) {
|
|
1075
|
+
const domainKey = this.getDomainKey(domain);
|
|
1076
|
+
const displayName = this.getDisplayName(domain);
|
|
1077
|
+
return {
|
|
1078
|
+
name: domainKey,
|
|
1079
|
+
displayName,
|
|
1080
|
+
domains: {
|
|
1081
|
+
production: domain,
|
|
1082
|
+
staging: domain
|
|
1083
|
+
},
|
|
1084
|
+
services: {
|
|
1085
|
+
frontend: {
|
|
1086
|
+
production: `https://${domain}`,
|
|
1087
|
+
staging: `https://${domain}`
|
|
1088
|
+
},
|
|
1089
|
+
api: {
|
|
1090
|
+
production: `https://${domainKey}-data-service.tamylatrading.workers.dev`,
|
|
1091
|
+
staging: `https://${domainKey}-data-service-staging.tamylatrading.workers.dev`
|
|
1092
|
+
}
|
|
1093
|
+
},
|
|
1094
|
+
corsOrigins: {
|
|
1095
|
+
production: [`https://${domain}`, `https://*.${domain}`],
|
|
1096
|
+
staging: [`https://${domain}`, `https://*.${domain}`]
|
|
1097
|
+
},
|
|
1098
|
+
databases: {
|
|
1099
|
+
production: {
|
|
1100
|
+
name: `${domainKey}-auth-db`,
|
|
1101
|
+
id: null
|
|
1102
|
+
},
|
|
1103
|
+
staging: {
|
|
1104
|
+
name: `${domainKey}-auth-db-staging`,
|
|
1105
|
+
id: null
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
};
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
// Legacy function exports for backward compatibility
|
|
1113
|
+
|
|
1114
|
+
/**
|
|
1115
|
+
* Simple configuration cache
|
|
1116
|
+
* @param {string} domain - Domain name
|
|
1117
|
+
* @param {Object} options - Cache options
|
|
1118
|
+
* @returns {ConfigurationCacheManager} Cache manager instance
|
|
1119
|
+
*/
|
|
1120
|
+
export function createConfigCache(domain, options = {}) {
|
|
1121
|
+
const cache = new ConfigurationCacheManager(options);
|
|
1122
|
+
return {
|
|
1123
|
+
get: env => cache.getOrCreateDomainConfig(domain, {
|
|
1124
|
+
environment: env
|
|
1125
|
+
}),
|
|
1126
|
+
invalidate: env => cache.invalidateCache(domain, env),
|
|
1127
|
+
save: (config, env) => cache.saveConfiguration(domain, config, env),
|
|
1128
|
+
validate: config => cache.validateConfiguration(config)
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
/**
|
|
1133
|
+
* Generate configuration from template
|
|
1134
|
+
* @param {string} domain - Domain name
|
|
1135
|
+
* @param {string} templateName - Template name
|
|
1136
|
+
* @param {Object} values - Template values
|
|
1137
|
+
* @returns {Promise<Object>} Generated configuration
|
|
1138
|
+
*/
|
|
1139
|
+
export async function generateConfig(domain, templateName = 'domain-standard', values = {}) {
|
|
1140
|
+
const cache = new ConfigurationCacheManager();
|
|
1141
|
+
return cache.generateFromTemplate(domain, {
|
|
1142
|
+
templateName,
|
|
1143
|
+
customValues: values
|
|
1144
|
+
});
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
/**
|
|
1148
|
+
* Discover domain configuration
|
|
1149
|
+
* @param {string} domain - Domain name
|
|
1150
|
+
* @param {string} cloudflareToken - Cloudflare API token
|
|
1151
|
+
* @returns {Promise<Object>} Discovered configuration
|
|
1152
|
+
*/
|
|
1153
|
+
export async function discoverConfig(domain, cloudflareToken) {
|
|
1154
|
+
const cache = new ConfigurationCacheManager({
|
|
1155
|
+
enableRuntimeDiscovery: true
|
|
1156
|
+
});
|
|
1157
|
+
return cache.discoverDomainConfiguration(domain, {
|
|
1158
|
+
cloudflareToken
|
|
1159
|
+
});
|
|
1160
|
+
}
|