@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,638 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ServiceOrchestrator - Unified Three-Tier Service Management
|
|
3
|
+
*
|
|
4
|
+
* Coordinates the three-tier service creation process:
|
|
5
|
+
* 1. Core Input Collection (6 required inputs)
|
|
6
|
+
* 2. Smart Confirmations (15 derived values)
|
|
7
|
+
* 3. Automated Generation (67 configurations + service manifest)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// Modular handler imports
|
|
11
|
+
import { InputHandler } from './handlers/InputHandler.js';
|
|
12
|
+
import { ConfirmationHandler } from './handlers/ConfirmationHandler.js';
|
|
13
|
+
import { GenerationHandler } from './handlers/GenerationHandler.js';
|
|
14
|
+
import { ConfigMutator } from './handlers/ConfigMutator.js';
|
|
15
|
+
import { ValidationHandler } from './handlers/ValidationHandler.js';
|
|
16
|
+
|
|
17
|
+
// Legacy imports for backward compatibility
|
|
18
|
+
import { ServiceCreator } from './ServiceCreator.js';
|
|
19
|
+
import { ErrorTracker } from './ErrorTracker.js';
|
|
20
|
+
import chalk from 'chalk';
|
|
21
|
+
import fs from 'fs/promises';
|
|
22
|
+
import path from 'path';
|
|
23
|
+
export class ServiceOrchestrator {
|
|
24
|
+
constructor(options = {}) {
|
|
25
|
+
this.interactive = options.interactive !== false;
|
|
26
|
+
this.outputPath = options.outputPath || '.';
|
|
27
|
+
this.templatePath = options.templatePath || './templates';
|
|
28
|
+
|
|
29
|
+
// Initialize modular handler components
|
|
30
|
+
this.inputHandler = new InputHandler({
|
|
31
|
+
interactive: this.interactive
|
|
32
|
+
});
|
|
33
|
+
this.confirmationHandler = new ConfirmationHandler({
|
|
34
|
+
interactive: this.interactive
|
|
35
|
+
});
|
|
36
|
+
this.generationHandler = new GenerationHandler({
|
|
37
|
+
outputPath: this.outputPath,
|
|
38
|
+
templatePath: this.templatePath
|
|
39
|
+
});
|
|
40
|
+
this.configMutator = new ConfigMutator();
|
|
41
|
+
this.validationHandler = new ValidationHandler();
|
|
42
|
+
|
|
43
|
+
// Initialize legacy components for backward compatibility
|
|
44
|
+
this.serviceCreator = new ServiceCreator();
|
|
45
|
+
this.errorTracker = new ErrorTracker();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Run the complete three-tier service creation process interactively
|
|
50
|
+
*/
|
|
51
|
+
async runInteractive() {
|
|
52
|
+
console.log(chalk.cyan('🚀 Clodo Framework - Interactive Service Creator'));
|
|
53
|
+
console.log(chalk.white('Welcome to the unified service creation wizard!\n'));
|
|
54
|
+
try {
|
|
55
|
+
// Tier 1: Collect 6 core inputs
|
|
56
|
+
console.log(chalk.yellow('📝 Tier 1: Core Input Collection'));
|
|
57
|
+
console.log(chalk.white('Collecting 6 required inputs for your service...\n'));
|
|
58
|
+
const coreInputs = await this.inputHandler.collectCoreInputs();
|
|
59
|
+
|
|
60
|
+
// Tier 2: Smart confirmations for 15 derived values
|
|
61
|
+
const confirmedValues = await this.confirmationHandler.generateAndConfirm(coreInputs);
|
|
62
|
+
|
|
63
|
+
// Tier 3: Automated generation of 67 components
|
|
64
|
+
console.log(chalk.yellow('⚙️ Tier 3: Automated Generation'));
|
|
65
|
+
console.log(chalk.white('Generating 67 configuration files and service components...\n'));
|
|
66
|
+
const generationResult = await this.generationHandler.generateService(coreInputs, confirmedValues, {
|
|
67
|
+
outputPath: this.outputPath
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Display results
|
|
71
|
+
this.displayResults(generationResult);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
throw new Error(`Service creation failed: ${error.message}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Run service creation in non-interactive mode with provided inputs
|
|
79
|
+
*/
|
|
80
|
+
async runNonInteractive(coreInputs) {
|
|
81
|
+
console.log(chalk.cyan('🚀 Clodo Framework - Non-Interactive Service Creator'));
|
|
82
|
+
try {
|
|
83
|
+
// Validate inputs
|
|
84
|
+
await this.inputHandler.validateCoreInputs(coreInputs);
|
|
85
|
+
|
|
86
|
+
// Generate derived values automatically
|
|
87
|
+
const confirmedValues = await this.confirmationHandler.generateAndConfirm(coreInputs);
|
|
88
|
+
|
|
89
|
+
// Generate service using GenerationHandler
|
|
90
|
+
const generationResult = await this.generationHandler.generateService(coreInputs, confirmedValues, {
|
|
91
|
+
outputPath: this.outputPath
|
|
92
|
+
});
|
|
93
|
+
console.log(chalk.green(`✓ Service "${coreInputs.serviceName}" created successfully`));
|
|
94
|
+
} catch (error) {
|
|
95
|
+
throw new Error(`Non-interactive service creation failed: ${error.message}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Validate an existing service configuration
|
|
101
|
+
*/
|
|
102
|
+
async validateService(servicePath) {
|
|
103
|
+
return await this.validationHandler.validateService(servicePath);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Run comprehensive service diagnostics (delegated to ValidationHandler)
|
|
108
|
+
*/
|
|
109
|
+
async diagnoseServiceBasic(servicePath, options = {}) {
|
|
110
|
+
return await this.validationHandler.diagnoseService(servicePath);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Legacy validateService implementation for compatibility
|
|
115
|
+
*/
|
|
116
|
+
async _legacyValidateService(servicePath) {
|
|
117
|
+
try {
|
|
118
|
+
const issues = [];
|
|
119
|
+
|
|
120
|
+
// Check for required files
|
|
121
|
+
const requiredFiles = ['package.json', 'src/config/domains.js', 'src/worker/index.js', 'wrangler.toml'];
|
|
122
|
+
for (const file of requiredFiles) {
|
|
123
|
+
const filePath = path.join(servicePath, file);
|
|
124
|
+
try {
|
|
125
|
+
await fs.access(filePath);
|
|
126
|
+
} catch {
|
|
127
|
+
issues.push(`Missing required file: ${file}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Validate package.json
|
|
132
|
+
try {
|
|
133
|
+
const packageJson = JSON.parse(await fs.readFile(path.join(servicePath, 'package.json'), 'utf8'));
|
|
134
|
+
if (!packageJson.name || !packageJson.version) {
|
|
135
|
+
issues.push('Invalid package.json: missing name or version');
|
|
136
|
+
}
|
|
137
|
+
} catch {
|
|
138
|
+
issues.push('Invalid package.json format');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Validate domain configuration
|
|
142
|
+
try {
|
|
143
|
+
const domainConfig = await fs.readFile(path.join(servicePath, 'src/config/domains.js'), 'utf8');
|
|
144
|
+
if (!domainConfig.includes('createDomainConfigSchema')) {
|
|
145
|
+
issues.push('Domain configuration missing Clodo Framework integration');
|
|
146
|
+
}
|
|
147
|
+
} catch {
|
|
148
|
+
issues.push('Cannot read domain configuration');
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
valid: issues.length === 0,
|
|
152
|
+
issues
|
|
153
|
+
};
|
|
154
|
+
} catch (error) {
|
|
155
|
+
throw new Error(`Service validation failed: ${error.message}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Display generation results to user
|
|
161
|
+
*/
|
|
162
|
+
displayResults(generationResult) {
|
|
163
|
+
console.log(chalk.green('\n✅ Service Generation Complete!'));
|
|
164
|
+
if (generationResult.serviceManifest) {
|
|
165
|
+
// New GenerationEngine format
|
|
166
|
+
console.log(chalk.white(`Service: ${generationResult.serviceName}`));
|
|
167
|
+
console.log(chalk.white(`Path: ${generationResult.servicePath}`));
|
|
168
|
+
console.log(chalk.white(`Files Generated: ${generationResult.fileCount}`));
|
|
169
|
+
console.log(chalk.cyan('\n📁 Generated Files:'));
|
|
170
|
+
generationResult.generatedFiles.forEach(file => {
|
|
171
|
+
console.log(chalk.white(` ✓ ${path.relative(process.cwd(), file)}`));
|
|
172
|
+
});
|
|
173
|
+
console.log(chalk.cyan('\n📋 Service Manifest:'));
|
|
174
|
+
console.log(chalk.white(` Location: clodo-service-manifest.json`));
|
|
175
|
+
} else {
|
|
176
|
+
// Legacy format (for backward compatibility)
|
|
177
|
+
console.log(chalk.white(`Service: ${generationResult.serviceName}`));
|
|
178
|
+
console.log(chalk.white(`Type: ${generationResult.serviceType}`));
|
|
179
|
+
console.log(chalk.white(`Domain: ${generationResult.domainName}`));
|
|
180
|
+
console.log(chalk.white(`Environment: ${generationResult.environment}`));
|
|
181
|
+
console.log(chalk.cyan('\n📁 Generated Files:'));
|
|
182
|
+
generationResult.generatedFiles.forEach(file => {
|
|
183
|
+
console.log(chalk.white(` ✓ ${file}`));
|
|
184
|
+
});
|
|
185
|
+
console.log(chalk.cyan('\n🔧 Configured Features:'));
|
|
186
|
+
generationResult.features.forEach(feature => {
|
|
187
|
+
console.log(chalk.white(` ✓ ${feature}`));
|
|
188
|
+
});
|
|
189
|
+
if (generationResult.serviceManifest) {
|
|
190
|
+
console.log(chalk.cyan('\n📋 Service Manifest:'));
|
|
191
|
+
console.log(chalk.white(` Location: ${generationResult.serviceManifest}`));
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Auto-detect service path from current working directory
|
|
198
|
+
*/
|
|
199
|
+
async detectServicePath() {
|
|
200
|
+
try {
|
|
201
|
+
// Check if current directory is a service
|
|
202
|
+
const currentDir = process.cwd();
|
|
203
|
+
if (await this.isServiceDirectory(currentDir)) {
|
|
204
|
+
return currentDir;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Check parent directories
|
|
208
|
+
let checkDir = path.dirname(currentDir);
|
|
209
|
+
while (checkDir !== path.dirname(checkDir)) {
|
|
210
|
+
// Stop at root
|
|
211
|
+
if (await this.isServiceDirectory(checkDir)) {
|
|
212
|
+
return checkDir;
|
|
213
|
+
}
|
|
214
|
+
checkDir = path.dirname(checkDir);
|
|
215
|
+
}
|
|
216
|
+
return null;
|
|
217
|
+
} catch (error) {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Check if a directory is a Clodo service
|
|
224
|
+
*/
|
|
225
|
+
async isServiceDirectory(dirPath) {
|
|
226
|
+
try {
|
|
227
|
+
const requiredFiles = ['package.json', 'src/config/domains.js', 'wrangler.toml'];
|
|
228
|
+
for (const file of requiredFiles) {
|
|
229
|
+
await fs.access(path.join(dirPath, file));
|
|
230
|
+
}
|
|
231
|
+
return true;
|
|
232
|
+
} catch {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Run interactive service update
|
|
239
|
+
*/
|
|
240
|
+
async runInteractiveUpdate(servicePath) {
|
|
241
|
+
console.log(chalk.cyan('🔄 Interactive Service Update'));
|
|
242
|
+
console.log(chalk.white(`Updating service at: ${servicePath}\n`));
|
|
243
|
+
|
|
244
|
+
// Load current service configuration
|
|
245
|
+
const currentConfig = await this.loadServiceConfiguration(servicePath);
|
|
246
|
+
console.log(chalk.cyan('Current Configuration:'));
|
|
247
|
+
console.log(chalk.white(` Service: ${currentConfig.serviceName}`));
|
|
248
|
+
console.log(chalk.white(` Type: ${currentConfig.serviceType}`));
|
|
249
|
+
console.log(chalk.white(` Domain: ${currentConfig.domainName}`));
|
|
250
|
+
console.log(chalk.white(` Environment: ${currentConfig.environment}\n`));
|
|
251
|
+
|
|
252
|
+
// Interactive update menu
|
|
253
|
+
const updateOptions = {
|
|
254
|
+
1: {
|
|
255
|
+
name: 'Domain Configuration',
|
|
256
|
+
action: () => this.updateDomainConfig(servicePath, currentConfig)
|
|
257
|
+
},
|
|
258
|
+
2: {
|
|
259
|
+
name: 'Cloudflare Settings',
|
|
260
|
+
action: () => this.updateCloudflareConfig(servicePath, currentConfig)
|
|
261
|
+
},
|
|
262
|
+
3: {
|
|
263
|
+
name: 'Environment Settings',
|
|
264
|
+
action: () => this.updateEnvironmentConfig(servicePath, currentConfig)
|
|
265
|
+
},
|
|
266
|
+
4: {
|
|
267
|
+
name: 'Feature Flags',
|
|
268
|
+
action: () => this.updateFeatureFlags(servicePath, currentConfig)
|
|
269
|
+
},
|
|
270
|
+
5: {
|
|
271
|
+
name: 'Regenerate All Configs',
|
|
272
|
+
action: () => this.regenerateAllConfigs(servicePath, currentConfig)
|
|
273
|
+
},
|
|
274
|
+
6: {
|
|
275
|
+
name: 'Fix Configuration Errors',
|
|
276
|
+
action: () => this.fixConfigurationErrors(servicePath)
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
for (;;) {
|
|
280
|
+
console.log(chalk.cyan('What would you like to update?'));
|
|
281
|
+
Object.entries(updateOptions).forEach(([key, option]) => {
|
|
282
|
+
console.log(chalk.white(` ${key}. ${option.name}`));
|
|
283
|
+
});
|
|
284
|
+
console.log(chalk.white(' 0. Exit update mode\n'));
|
|
285
|
+
const choice = await this.promptUser('Enter your choice (0-6): ');
|
|
286
|
+
if (choice === '0') {
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
const option = updateOptions[choice];
|
|
290
|
+
if (option) {
|
|
291
|
+
try {
|
|
292
|
+
await option.action();
|
|
293
|
+
console.log(chalk.green(`✓ ${option.name} updated successfully`));
|
|
294
|
+
} catch (error) {
|
|
295
|
+
this.errorTracker.captureError(error, {
|
|
296
|
+
action: option.name,
|
|
297
|
+
servicePath
|
|
298
|
+
});
|
|
299
|
+
console.log(chalk.red(`✗ Failed to update ${option.name}: ${error.message}`));
|
|
300
|
+
}
|
|
301
|
+
} else {
|
|
302
|
+
console.log(chalk.red('Invalid choice. Please select 0-6.'));
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Run non-interactive service update
|
|
309
|
+
*/
|
|
310
|
+
async runNonInteractiveUpdate(servicePath, options) {
|
|
311
|
+
console.log(chalk.cyan('🔄 Non-Interactive Service Update'));
|
|
312
|
+
const currentConfig = await this.loadServiceConfiguration(servicePath);
|
|
313
|
+
let hasChanges = false;
|
|
314
|
+
try {
|
|
315
|
+
// Update domain if specified
|
|
316
|
+
if (options.domainName) {
|
|
317
|
+
await this.updateDomainConfig(servicePath, currentConfig, {
|
|
318
|
+
domainName: options.domainName
|
|
319
|
+
});
|
|
320
|
+
hasChanges = true;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Update Cloudflare settings
|
|
324
|
+
if (options.cloudflareToken || options.cloudflareAccountId || options.cloudflareZoneId) {
|
|
325
|
+
const cfUpdates = {};
|
|
326
|
+
if (options.cloudflareToken) cfUpdates.token = options.cloudflareToken;
|
|
327
|
+
if (options.cloudflareAccountId) cfUpdates.accountId = options.cloudflareAccountId;
|
|
328
|
+
if (options.cloudflareZoneId) cfUpdates.zoneId = options.cloudflareZoneId;
|
|
329
|
+
await this.updateCloudflareConfig(servicePath, currentConfig, cfUpdates);
|
|
330
|
+
hasChanges = true;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Update environment
|
|
334
|
+
if (options.environment) {
|
|
335
|
+
await this.updateEnvironmentConfig(servicePath, currentConfig, {
|
|
336
|
+
environment: options.environment
|
|
337
|
+
});
|
|
338
|
+
hasChanges = true;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Update features
|
|
342
|
+
if (options.addFeature || options.removeFeature) {
|
|
343
|
+
const featureUpdates = {};
|
|
344
|
+
if (options.addFeature) featureUpdates.add = options.addFeature;
|
|
345
|
+
if (options.removeFeature) featureUpdates.remove = options.removeFeature;
|
|
346
|
+
await this.updateFeatureFlags(servicePath, currentConfig, featureUpdates);
|
|
347
|
+
hasChanges = true;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Regenerate configs if requested
|
|
351
|
+
if (options.regenerateConfigs) {
|
|
352
|
+
await this.regenerateAllConfigs(servicePath, currentConfig);
|
|
353
|
+
hasChanges = true;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Fix errors if requested
|
|
357
|
+
if (options.fixErrors) {
|
|
358
|
+
await this.fixConfigurationErrors(servicePath);
|
|
359
|
+
hasChanges = true;
|
|
360
|
+
}
|
|
361
|
+
if (!hasChanges) {
|
|
362
|
+
console.log(chalk.yellow('No update options specified. Use --help to see available options.'));
|
|
363
|
+
}
|
|
364
|
+
} catch (error) {
|
|
365
|
+
this.errorTracker.captureError(error, {
|
|
366
|
+
options,
|
|
367
|
+
servicePath
|
|
368
|
+
});
|
|
369
|
+
throw error;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Load current service configuration
|
|
375
|
+
*/
|
|
376
|
+
async loadServiceConfiguration(servicePath) {
|
|
377
|
+
try {
|
|
378
|
+
// Load package.json
|
|
379
|
+
const packageJson = JSON.parse(await fs.readFile(path.join(servicePath, 'package.json'), 'utf8'));
|
|
380
|
+
|
|
381
|
+
// Load domain configuration
|
|
382
|
+
const domainConfig = await fs.readFile(path.join(servicePath, 'src/config/domains.js'), 'utf8');
|
|
383
|
+
|
|
384
|
+
// Extract configuration from domain config (simplified parsing)
|
|
385
|
+
const config = {
|
|
386
|
+
serviceName: packageJson.name,
|
|
387
|
+
serviceType: this.extractServiceTypeFromConfig(domainConfig),
|
|
388
|
+
domainName: this.extractDomainFromConfig(domainConfig),
|
|
389
|
+
environment: this.extractEnvironmentFromConfig(domainConfig),
|
|
390
|
+
cloudflareAccountId: this.extractCloudflareAccountId(domainConfig),
|
|
391
|
+
cloudflareZoneId: this.extractCloudflareZoneId(domainConfig),
|
|
392
|
+
features: this.extractFeaturesFromConfig(domainConfig)
|
|
393
|
+
};
|
|
394
|
+
return config;
|
|
395
|
+
} catch (error) {
|
|
396
|
+
throw new Error(`Failed to load service configuration: ${error.message}`);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Update domain configuration using ConfigMutator
|
|
402
|
+
*/
|
|
403
|
+
async updateDomainConfig(servicePath, currentConfig, updates = null) {
|
|
404
|
+
if (!updates && this.interactive) {
|
|
405
|
+
// Interactive mode - use confirmation handler
|
|
406
|
+
const newDomain = await this.confirmationHandler.promptHandler.prompt(`Current domain: ${currentConfig.domainName}\nNew domain name: `);
|
|
407
|
+
if (!newDomain || newDomain === currentConfig.domainName) {
|
|
408
|
+
console.log(chalk.yellow('Domain unchanged'));
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
updates = {
|
|
412
|
+
domainName: newDomain
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
return await this.configMutator.updateDomainConfig(servicePath, currentConfig, updates);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Update Cloudflare configuration using ConfigMutator
|
|
420
|
+
*/
|
|
421
|
+
async updateCloudflareConfig(servicePath, currentConfig, updates = null) {
|
|
422
|
+
return await this.configMutator.updateCloudflareConfig(servicePath, currentConfig, updates);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Update environment configuration using ConfigMutator
|
|
427
|
+
*/
|
|
428
|
+
async updateEnvironmentConfig(servicePath, currentConfig, updates = null) {
|
|
429
|
+
return await this.configMutator.updateEnvironmentConfig(servicePath, currentConfig, updates);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Update feature flags using ConfigMutator
|
|
434
|
+
*/
|
|
435
|
+
async updateFeatureFlags(servicePath, currentConfig, updates = null) {
|
|
436
|
+
return await this.configMutator.updateFeatureConfig(servicePath, currentConfig, updates);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Regenerate all configuration files
|
|
441
|
+
*/
|
|
442
|
+
async regenerateAllConfigs(servicePath, currentConfig) {
|
|
443
|
+
console.log(chalk.cyan('Regenerating all configuration files...'));
|
|
444
|
+
|
|
445
|
+
// This would call the generation engine to recreate all configs
|
|
446
|
+
// For now, just mark as needing implementation
|
|
447
|
+
console.log(chalk.yellow('⚠️ Config regeneration not yet implemented'));
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Fix common configuration errors
|
|
452
|
+
*/
|
|
453
|
+
async fixConfigurationErrors(servicePath) {
|
|
454
|
+
console.log(chalk.cyan('Attempting to fix configuration errors...'));
|
|
455
|
+
const issues = await this.validateService(servicePath);
|
|
456
|
+
if (issues.valid) {
|
|
457
|
+
console.log(chalk.green('No issues found to fix'));
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Attempt to fix common issues
|
|
462
|
+
for (const issue of issues.issues) {
|
|
463
|
+
try {
|
|
464
|
+
if (issue.includes('Missing required file')) {
|
|
465
|
+
console.log(chalk.yellow(`Cannot auto-fix missing file: ${issue}`));
|
|
466
|
+
} else if (issue.includes('Invalid package.json')) {
|
|
467
|
+
console.log(chalk.yellow(`Cannot auto-fix package.json: ${issue}`));
|
|
468
|
+
} else {
|
|
469
|
+
console.log(chalk.yellow(`Unknown issue, cannot auto-fix: ${issue}`));
|
|
470
|
+
}
|
|
471
|
+
} catch (error) {
|
|
472
|
+
console.log(chalk.red(`Failed to fix issue: ${error.message}`));
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* Diagnose service issues
|
|
479
|
+
*/
|
|
480
|
+
async diagnoseService(servicePath, options = {}) {
|
|
481
|
+
console.log(chalk.cyan('🔍 Running comprehensive service diagnosis...'));
|
|
482
|
+
const diagnosis = {
|
|
483
|
+
serviceName: null,
|
|
484
|
+
errors: [],
|
|
485
|
+
warnings: [],
|
|
486
|
+
recommendations: []
|
|
487
|
+
};
|
|
488
|
+
try {
|
|
489
|
+
// Load basic configuration
|
|
490
|
+
const config = await this.loadServiceConfiguration(servicePath);
|
|
491
|
+
diagnosis.serviceName = config.serviceName;
|
|
492
|
+
|
|
493
|
+
// Check file structure
|
|
494
|
+
const requiredFiles = ['package.json', 'src/config/domains.js', 'src/worker/index.js', 'wrangler.toml'];
|
|
495
|
+
for (const file of requiredFiles) {
|
|
496
|
+
try {
|
|
497
|
+
await fs.access(path.join(servicePath, file));
|
|
498
|
+
} catch {
|
|
499
|
+
diagnosis.errors.push({
|
|
500
|
+
message: `Missing required file: ${file}`,
|
|
501
|
+
location: servicePath,
|
|
502
|
+
suggestion: `Run 'clodo-service update --regenerate-configs' to recreate missing files`
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// Validate package.json
|
|
508
|
+
try {
|
|
509
|
+
const packageJson = JSON.parse(await fs.readFile(path.join(servicePath, 'package.json'), 'utf8'));
|
|
510
|
+
if (!packageJson.name) {
|
|
511
|
+
diagnosis.errors.push({
|
|
512
|
+
message: 'package.json missing name field',
|
|
513
|
+
location: 'package.json',
|
|
514
|
+
suggestion: 'Add a name field to package.json'
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
} catch (error) {
|
|
518
|
+
diagnosis.errors.push({
|
|
519
|
+
message: `Invalid package.json: ${error.message}`,
|
|
520
|
+
location: 'package.json',
|
|
521
|
+
suggestion: 'Fix JSON syntax in package.json'
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// Check domain configuration
|
|
526
|
+
try {
|
|
527
|
+
const domainConfig = await fs.readFile(path.join(servicePath, 'src/config/domains.js'), 'utf8');
|
|
528
|
+
if (!domainConfig.includes('createDomainConfigSchema')) {
|
|
529
|
+
diagnosis.warnings.push({
|
|
530
|
+
message: 'Domain configuration may not be using Clodo Framework schema',
|
|
531
|
+
location: 'src/config/domains.js',
|
|
532
|
+
suggestion: 'Ensure domain config uses createDomainConfigSchema from @tamyla/clodo-framework'
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
} catch (error) {
|
|
536
|
+
diagnosis.errors.push({
|
|
537
|
+
message: 'Cannot read domain configuration',
|
|
538
|
+
location: 'src/config/domains.js',
|
|
539
|
+
suggestion: 'Check file permissions and syntax'
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// Deep scan if requested
|
|
544
|
+
if (options.deepScan) {
|
|
545
|
+
// Check for common issues
|
|
546
|
+
diagnosis.recommendations.push('Consider running tests to validate service functionality');
|
|
547
|
+
diagnosis.recommendations.push('Check Cloudflare authentication if deployment fails');
|
|
548
|
+
diagnosis.recommendations.push('Verify all dependencies are installed with npm install');
|
|
549
|
+
}
|
|
550
|
+
} catch (error) {
|
|
551
|
+
diagnosis.errors.push({
|
|
552
|
+
message: `Diagnosis failed: ${error.message}`,
|
|
553
|
+
location: 'general',
|
|
554
|
+
suggestion: 'Check service directory structure and permissions'
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
return diagnosis;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Export diagnostic report
|
|
562
|
+
*/
|
|
563
|
+
async exportDiagnosticReport(diagnosis, filePath) {
|
|
564
|
+
const report = {
|
|
565
|
+
timestamp: new Date().toISOString(),
|
|
566
|
+
serviceName: diagnosis.serviceName,
|
|
567
|
+
summary: {
|
|
568
|
+
errors: diagnosis.errors.length,
|
|
569
|
+
warnings: diagnosis.warnings.length,
|
|
570
|
+
recommendations: diagnosis.recommendations.length
|
|
571
|
+
},
|
|
572
|
+
errors: diagnosis.errors,
|
|
573
|
+
warnings: diagnosis.warnings,
|
|
574
|
+
recommendations: diagnosis.recommendations
|
|
575
|
+
};
|
|
576
|
+
await fs.writeFile(filePath, JSON.stringify(report, null, 2), 'utf8');
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Generate service using legacy ServiceCreator (placeholder for GenerationEngine)
|
|
581
|
+
*/
|
|
582
|
+
async generateWithLegacyCreator({
|
|
583
|
+
coreInputs,
|
|
584
|
+
confirmedValues,
|
|
585
|
+
outputPath,
|
|
586
|
+
templatePath
|
|
587
|
+
}) {
|
|
588
|
+
console.log(chalk.gray('Using legacy ServiceCreator for generation...\n'));
|
|
589
|
+
|
|
590
|
+
// This would normally use the GenerationEngine, but for now we'll create a basic result
|
|
591
|
+
return {
|
|
592
|
+
serviceName: coreInputs.serviceName,
|
|
593
|
+
serviceType: coreInputs.serviceType,
|
|
594
|
+
domainName: coreInputs.domainName,
|
|
595
|
+
environment: coreInputs.environment,
|
|
596
|
+
generatedFiles: ['package.json', 'src/config/domains.js', 'src/worker/index.js', 'wrangler.toml', 'README.md'],
|
|
597
|
+
features: Object.keys(confirmedValues.features || {}),
|
|
598
|
+
serviceManifest: null // TODO: Generate service manifest
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* Prompt user for input using shared PromptHandler
|
|
604
|
+
*/
|
|
605
|
+
async promptUser(question) {
|
|
606
|
+
if (!this.interactive) {
|
|
607
|
+
return '';
|
|
608
|
+
}
|
|
609
|
+
return await this.confirmationHandler.promptHandler.prompt(question);
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// Helper methods for extracting config values (simplified implementations)
|
|
613
|
+
extractServiceTypeFromConfig(config) {
|
|
614
|
+
return 'generic';
|
|
615
|
+
}
|
|
616
|
+
extractDomainFromConfig(config) {
|
|
617
|
+
return 'unknown';
|
|
618
|
+
}
|
|
619
|
+
extractEnvironmentFromConfig(config) {
|
|
620
|
+
return 'development';
|
|
621
|
+
}
|
|
622
|
+
extractCloudflareAccountId(config) {
|
|
623
|
+
return null;
|
|
624
|
+
}
|
|
625
|
+
extractCloudflareZoneId(config) {
|
|
626
|
+
return null;
|
|
627
|
+
}
|
|
628
|
+
extractFeaturesFromConfig(config) {
|
|
629
|
+
return [];
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* Escape special regex characters for safe replacement
|
|
634
|
+
*/
|
|
635
|
+
escapeRegExp(string) {
|
|
636
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
637
|
+
}
|
|
638
|
+
}
|