@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,3109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GenerationEngine - Tier 3: Automated Generation
|
|
3
|
+
*
|
|
4
|
+
* Takes the 6 core inputs and 15 confirmed values to automatically generate
|
|
5
|
+
* 67+ configuration files and components for a complete Clodo Framework service.
|
|
6
|
+
*
|
|
7
|
+
* Core Inputs (6):
|
|
8
|
+
* 1. Service Name
|
|
9
|
+
* 2. Service Type
|
|
10
|
+
* 3. Domain Name
|
|
11
|
+
* 4. Cloudflare Token
|
|
12
|
+
* 5. Cloudflare Account ID
|
|
13
|
+
* 6. Cloudflare Zone ID
|
|
14
|
+
* 7. Environment
|
|
15
|
+
*
|
|
16
|
+
* Confirmed Values (15):
|
|
17
|
+
* 1. Display Name
|
|
18
|
+
* 2. Description
|
|
19
|
+
* 3. Version
|
|
20
|
+
* 4. Author
|
|
21
|
+
* 5. Production URL
|
|
22
|
+
* 6. Staging URL
|
|
23
|
+
* 7. Development URL
|
|
24
|
+
* 8. Features Configuration
|
|
25
|
+
* 9. Database Name
|
|
26
|
+
* 10. Worker Name
|
|
27
|
+
* 11. Package Name
|
|
28
|
+
* 12. Git Repository URL
|
|
29
|
+
* 13. Documentation URL
|
|
30
|
+
* 14. Health Check Path
|
|
31
|
+
* 15. API Base Path
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, cpSync } from 'fs';
|
|
35
|
+
import { join, dirname, resolve, relative } from 'path';
|
|
36
|
+
import { fileURLToPath } from 'url';
|
|
37
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
38
|
+
const __dirname = dirname(__filename);
|
|
39
|
+
export class GenerationEngine {
|
|
40
|
+
constructor(options = {}) {
|
|
41
|
+
this.templatesDir = options.templatesDir || join(__dirname, '..', '..', 'templates');
|
|
42
|
+
this.outputDir = options.outputDir || process.cwd();
|
|
43
|
+
this.force = options.force || false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Generate complete service from core inputs and confirmed values
|
|
48
|
+
*/
|
|
49
|
+
async generateService(coreInputs, confirmedValues, options = {}) {
|
|
50
|
+
const config = {
|
|
51
|
+
outputPath: this.outputDir,
|
|
52
|
+
...options
|
|
53
|
+
};
|
|
54
|
+
console.log('⚙️ Tier 3: Automated Generation');
|
|
55
|
+
console.log('Generating 67+ configuration files and service components...\n');
|
|
56
|
+
try {
|
|
57
|
+
const servicePath = join(config.outputPath, coreInputs.serviceName);
|
|
58
|
+
|
|
59
|
+
// Create service directory structure
|
|
60
|
+
this.createDirectoryStructure(servicePath);
|
|
61
|
+
|
|
62
|
+
// Generate all configuration files
|
|
63
|
+
const generatedFiles = await this.generateAllFiles(coreInputs, confirmedValues, servicePath);
|
|
64
|
+
|
|
65
|
+
// Create service manifest
|
|
66
|
+
const serviceManifest = this.createServiceManifest(coreInputs, confirmedValues, generatedFiles);
|
|
67
|
+
|
|
68
|
+
// Write service manifest
|
|
69
|
+
const manifestPath = join(servicePath, 'clodo-service-manifest.json');
|
|
70
|
+
writeFileSync(manifestPath, JSON.stringify(serviceManifest, null, 2), 'utf8');
|
|
71
|
+
generatedFiles.push(manifestPath);
|
|
72
|
+
console.log(`✅ Generated ${generatedFiles.length} files successfully`);
|
|
73
|
+
console.log(`📋 Service manifest created: clodo-service-manifest.json`);
|
|
74
|
+
return {
|
|
75
|
+
success: true,
|
|
76
|
+
serviceName: coreInputs.serviceName,
|
|
77
|
+
servicePath,
|
|
78
|
+
generatedFiles,
|
|
79
|
+
serviceManifest,
|
|
80
|
+
fileCount: generatedFiles.length
|
|
81
|
+
};
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error(`❌ Generation failed: ${error.message}`);
|
|
84
|
+
throw new Error(`Service generation failed: ${error.message}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Create the complete directory structure
|
|
90
|
+
*/
|
|
91
|
+
createDirectoryStructure(servicePath) {
|
|
92
|
+
const directories = ['src', 'src/config', 'src/worker', 'src/handlers', 'src/middleware', 'src/utils', 'src/schemas', 'scripts', 'test', 'test/unit', 'test/integration', 'docs', 'config', 'logs', '.github', '.github/workflows'];
|
|
93
|
+
for (const dir of directories) {
|
|
94
|
+
const fullPath = join(servicePath, dir);
|
|
95
|
+
if (!existsSync(fullPath)) {
|
|
96
|
+
mkdirSync(fullPath, {
|
|
97
|
+
recursive: true
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Generate all configuration files
|
|
105
|
+
*/
|
|
106
|
+
async generateAllFiles(coreInputs, confirmedValues, servicePath) {
|
|
107
|
+
const generatedFiles = [];
|
|
108
|
+
|
|
109
|
+
// Core configuration files
|
|
110
|
+
generatedFiles.push(...this.generateCoreFiles(coreInputs, confirmedValues, servicePath));
|
|
111
|
+
|
|
112
|
+
// Service-specific configuration files
|
|
113
|
+
generatedFiles.push(...this.generateServiceSpecificFiles(coreInputs, confirmedValues, servicePath));
|
|
114
|
+
|
|
115
|
+
// Environment and deployment files
|
|
116
|
+
generatedFiles.push(...this.generateEnvironmentFiles(coreInputs, confirmedValues, servicePath));
|
|
117
|
+
|
|
118
|
+
// Testing and quality assurance files
|
|
119
|
+
generatedFiles.push(...this.generateTestingFiles(coreInputs, confirmedValues, servicePath));
|
|
120
|
+
|
|
121
|
+
// Documentation files
|
|
122
|
+
generatedFiles.push(...this.generateDocumentationFiles(coreInputs, confirmedValues, servicePath));
|
|
123
|
+
|
|
124
|
+
// CI/CD and automation files
|
|
125
|
+
generatedFiles.push(...this.generateAutomationFiles(coreInputs, confirmedValues, servicePath));
|
|
126
|
+
return generatedFiles;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Generate core configuration files (package.json, wrangler.toml, etc.)
|
|
131
|
+
*/
|
|
132
|
+
generateCoreFiles(coreInputs, confirmedValues, servicePath) {
|
|
133
|
+
const files = [];
|
|
134
|
+
|
|
135
|
+
// package.json
|
|
136
|
+
// files.push(this.generatePackageJson(coreInputs, confirmedValues, servicePath));
|
|
137
|
+
|
|
138
|
+
// wrangler.toml
|
|
139
|
+
// files.push(this.generateWranglerToml(coreInputs, confirmedValues, servicePath));
|
|
140
|
+
|
|
141
|
+
// src/config/domains.js
|
|
142
|
+
// files.push(this.generateDomainsConfig(coreInputs, confirmedValues, servicePath));
|
|
143
|
+
|
|
144
|
+
// src/worker/index.js
|
|
145
|
+
// files.push(this.generateWorkerIndex(coreInputs, confirmedValues, servicePath));
|
|
146
|
+
|
|
147
|
+
// .env.example
|
|
148
|
+
// files.push(this.generateEnvExample(coreInputs, confirmedValues, servicePath));
|
|
149
|
+
|
|
150
|
+
return files;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Generate service-specific configuration files
|
|
155
|
+
*/
|
|
156
|
+
generateServiceSpecificFiles(coreInputs, confirmedValues, servicePath) {
|
|
157
|
+
const files = [];
|
|
158
|
+
|
|
159
|
+
// src/schemas/service-schema.js
|
|
160
|
+
// files.push(this.generateServiceSchema(coreInputs, confirmedValues, servicePath));
|
|
161
|
+
|
|
162
|
+
// src/handlers/service-handlers.js
|
|
163
|
+
// files.push(this.generateServiceHandlers(coreInputs, confirmedValues, servicePath));
|
|
164
|
+
|
|
165
|
+
// src/middleware/service-middleware.js
|
|
166
|
+
// files.push(this.generateServiceMiddleware(coreInputs, confirmedValues, servicePath));
|
|
167
|
+
|
|
168
|
+
// src/utils/service-utils.js
|
|
169
|
+
// files.push(this.generateServiceUtils(coreInputs, confirmedValues, servicePath));
|
|
170
|
+
|
|
171
|
+
return files;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Generate environment and deployment files
|
|
176
|
+
*/
|
|
177
|
+
generateEnvironmentFiles(coreInputs, confirmedValues, servicePath) {
|
|
178
|
+
const files = [];
|
|
179
|
+
|
|
180
|
+
// scripts/deploy.ps1
|
|
181
|
+
files.push(this.generateDeployScript(coreInputs, confirmedValues, servicePath));
|
|
182
|
+
|
|
183
|
+
// scripts/setup.ps1
|
|
184
|
+
files.push(this.generateSetupScript(coreInputs, confirmedValues, servicePath));
|
|
185
|
+
|
|
186
|
+
// scripts/health-check.ps1
|
|
187
|
+
files.push(this.generateHealthCheckScript(coreInputs, confirmedValues, servicePath));
|
|
188
|
+
|
|
189
|
+
// config/production.env
|
|
190
|
+
files.push(this.generateProductionEnv(coreInputs, confirmedValues, servicePath));
|
|
191
|
+
|
|
192
|
+
// config/staging.env
|
|
193
|
+
files.push(this.generateStagingEnv(coreInputs, confirmedValues, servicePath));
|
|
194
|
+
|
|
195
|
+
// config/development.env
|
|
196
|
+
files.push(this.generateDevelopmentEnv(coreInputs, confirmedValues, servicePath));
|
|
197
|
+
return files;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Generate testing and quality assurance files
|
|
202
|
+
*/
|
|
203
|
+
generateTestingFiles(coreInputs, confirmedValues, servicePath) {
|
|
204
|
+
const files = [];
|
|
205
|
+
|
|
206
|
+
// test/unit/service.test.js
|
|
207
|
+
files.push(this.generateUnitTests(coreInputs, confirmedValues, servicePath));
|
|
208
|
+
|
|
209
|
+
// test/integration/service.integration.test.js
|
|
210
|
+
files.push(this.generateIntegrationTests(coreInputs, confirmedValues, servicePath));
|
|
211
|
+
|
|
212
|
+
// jest.config.js
|
|
213
|
+
files.push(this.generateJestConfig(coreInputs, confirmedValues, servicePath));
|
|
214
|
+
|
|
215
|
+
// .eslintrc.js
|
|
216
|
+
files.push(this.generateEslintConfig(coreInputs, confirmedValues, servicePath));
|
|
217
|
+
return files;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Generate documentation files
|
|
222
|
+
*/
|
|
223
|
+
generateDocumentationFiles(coreInputs, confirmedValues, servicePath) {
|
|
224
|
+
const files = [];
|
|
225
|
+
|
|
226
|
+
// README.md
|
|
227
|
+
files.push(this.generateReadme(coreInputs, confirmedValues, servicePath));
|
|
228
|
+
|
|
229
|
+
// docs/API.md
|
|
230
|
+
files.push(this.generateApiDocs(coreInputs, confirmedValues, servicePath));
|
|
231
|
+
|
|
232
|
+
// docs/DEPLOYMENT.md
|
|
233
|
+
files.push(this.generateDeploymentDocs(coreInputs, confirmedValues, servicePath));
|
|
234
|
+
|
|
235
|
+
// docs/CONFIGURATION.md
|
|
236
|
+
files.push(this.generateConfigurationDocs(coreInputs, confirmedValues, servicePath));
|
|
237
|
+
return files;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Generate automation and CI/CD files
|
|
242
|
+
*/
|
|
243
|
+
generateAutomationFiles(coreInputs, confirmedValues, servicePath) {
|
|
244
|
+
const files = [];
|
|
245
|
+
|
|
246
|
+
// .github/workflows/ci.yml
|
|
247
|
+
files.push(this.generateCiWorkflow(coreInputs, confirmedValues, servicePath));
|
|
248
|
+
|
|
249
|
+
// .github/workflows/deploy.yml
|
|
250
|
+
files.push(this.generateDeployWorkflow(coreInputs, confirmedValues, servicePath));
|
|
251
|
+
|
|
252
|
+
// .gitignore
|
|
253
|
+
files.push(this.generateGitignore(coreInputs, confirmedValues, servicePath));
|
|
254
|
+
|
|
255
|
+
// docker-compose.yml (for local development)
|
|
256
|
+
files.push(this.generateDockerCompose(coreInputs, confirmedValues, servicePath));
|
|
257
|
+
return files;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Generate package.json
|
|
262
|
+
*/
|
|
263
|
+
generatePackageJson(coreInputs, confirmedValues, servicePath) {
|
|
264
|
+
const packageJson = {
|
|
265
|
+
name: confirmedValues.packageName,
|
|
266
|
+
version: confirmedValues.version,
|
|
267
|
+
description: confirmedValues.description,
|
|
268
|
+
main: "src/worker/index.js",
|
|
269
|
+
type: "module",
|
|
270
|
+
scripts: {
|
|
271
|
+
test: "jest",
|
|
272
|
+
"test:watch": "jest --watch",
|
|
273
|
+
"test:coverage": "jest --coverage",
|
|
274
|
+
dev: "wrangler dev",
|
|
275
|
+
deploy: "powershell .\\scripts\\deploy.ps1",
|
|
276
|
+
setup: "powershell .\\scripts\\setup.ps1",
|
|
277
|
+
"health-check": "powershell .\\scripts\\health-check.ps1",
|
|
278
|
+
lint: "eslint src/ test/",
|
|
279
|
+
"lint:fix": "eslint src/ test/ --fix",
|
|
280
|
+
format: "prettier --write src/ test/",
|
|
281
|
+
build: "wrangler deploy --dry-run",
|
|
282
|
+
clean: "rimraf dist/ coverage/"
|
|
283
|
+
},
|
|
284
|
+
dependencies: {
|
|
285
|
+
"@tamyla/clodo-framework": "^3.0.0",
|
|
286
|
+
"wrangler": "^3.0.0"
|
|
287
|
+
},
|
|
288
|
+
devDependencies: {
|
|
289
|
+
"@types/jest": "^29.5.0",
|
|
290
|
+
"@types/node": "^20.0.0",
|
|
291
|
+
"eslint": "^8.54.0",
|
|
292
|
+
"jest": "^29.7.0",
|
|
293
|
+
"prettier": "^3.1.0",
|
|
294
|
+
"rimraf": "^5.0.0"
|
|
295
|
+
},
|
|
296
|
+
author: confirmedValues.author,
|
|
297
|
+
license: "MIT",
|
|
298
|
+
repository: {
|
|
299
|
+
type: "git",
|
|
300
|
+
url: confirmedValues.gitRepositoryUrl
|
|
301
|
+
},
|
|
302
|
+
keywords: ["clodo-framework", coreInputs.serviceType, "cloudflare", "serverless"],
|
|
303
|
+
engines: {
|
|
304
|
+
node: ">=18.0.0"
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
const filePath = join(servicePath, 'package.json');
|
|
308
|
+
writeFileSync(filePath, JSON.stringify(packageJson, null, 2), 'utf8');
|
|
309
|
+
return filePath;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Generate wrangler.toml
|
|
314
|
+
*/
|
|
315
|
+
generateWranglerToml(coreInputs, confirmedValues, servicePath) {
|
|
316
|
+
const wranglerToml = `# Cloudflare Workers Configuration for ${confirmedValues.displayName}
|
|
317
|
+
name = "${confirmedValues.workerName}"
|
|
318
|
+
main = "src/worker/index.js"
|
|
319
|
+
compatibility_date = "${new Date().toISOString().split('T')[0]}"
|
|
320
|
+
compatibility_flags = ["nodejs_compat"]
|
|
321
|
+
|
|
322
|
+
# Account configuration
|
|
323
|
+
account_id = "${coreInputs.cloudflareAccountId}"
|
|
324
|
+
|
|
325
|
+
# Environment configurations
|
|
326
|
+
[env.development]
|
|
327
|
+
name = "${confirmedValues.workerName}-dev"
|
|
328
|
+
|
|
329
|
+
[env.staging]
|
|
330
|
+
name = "${confirmedValues.workerName}-staging"
|
|
331
|
+
|
|
332
|
+
[env.production]
|
|
333
|
+
name = "${confirmedValues.workerName}"
|
|
334
|
+
|
|
335
|
+
# Database bindings
|
|
336
|
+
[[d1_databases]]
|
|
337
|
+
binding = "DB"
|
|
338
|
+
database_name = "${confirmedValues.databaseName}"
|
|
339
|
+
database_id = "" # To be configured during setup
|
|
340
|
+
|
|
341
|
+
# Environment variables
|
|
342
|
+
[vars]
|
|
343
|
+
SERVICE_NAME = "${coreInputs.serviceName}"
|
|
344
|
+
SERVICE_TYPE = "${coreInputs.serviceType}"
|
|
345
|
+
DOMAIN_NAME = "${coreInputs.domainName}"
|
|
346
|
+
ENVIRONMENT = "${coreInputs.environment}"
|
|
347
|
+
API_BASE_PATH = "${confirmedValues.apiBasePath}"
|
|
348
|
+
HEALTH_CHECK_PATH = "${confirmedValues.healthCheckPath}"
|
|
349
|
+
|
|
350
|
+
# Domain-specific variables
|
|
351
|
+
PRODUCTION_URL = "${confirmedValues.productionUrl}"
|
|
352
|
+
STAGING_URL = "${confirmedValues.stagingUrl}"
|
|
353
|
+
DEVELOPMENT_URL = "${confirmedValues.developmentUrl}"
|
|
354
|
+
|
|
355
|
+
# Feature flags
|
|
356
|
+
${Object.entries(confirmedValues.features).filter(([, enabled]) => enabled).map(([feature, enabled]) => `FEATURE_${feature.toUpperCase()} = ${enabled}`).join('\n')}
|
|
357
|
+
|
|
358
|
+
# Custom environment variables (configure as needed)
|
|
359
|
+
# CUSTOM_VAR = "value"
|
|
360
|
+
`;
|
|
361
|
+
const filePath = join(servicePath, 'wrangler.toml');
|
|
362
|
+
writeFileSync(filePath, wranglerToml, 'utf8');
|
|
363
|
+
return filePath;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Generate domains.js configuration
|
|
368
|
+
*/
|
|
369
|
+
generateDomainsConfig(coreInputs, confirmedValues, servicePath) {
|
|
370
|
+
const domainsConfig = `import { createDomainConfigSchema } from '@tamyla/clodo-framework';
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Domain configuration for ${confirmedValues.displayName}
|
|
374
|
+
*
|
|
375
|
+
* Generated by Clodo Framework GenerationEngine
|
|
376
|
+
* Service Type: ${coreInputs.serviceType}
|
|
377
|
+
* Generated: ${new Date().toISOString()}
|
|
378
|
+
*/
|
|
379
|
+
|
|
380
|
+
export const domains = {
|
|
381
|
+
'${coreInputs.serviceName}': {
|
|
382
|
+
...createDomainConfigSchema(),
|
|
383
|
+
name: '${coreInputs.serviceName}',
|
|
384
|
+
displayName: '${confirmedValues.displayName}',
|
|
385
|
+
description: '${confirmedValues.description}',
|
|
386
|
+
accountId: '${coreInputs.cloudflareAccountId}',
|
|
387
|
+
zoneId: '${coreInputs.cloudflareZoneId}',
|
|
388
|
+
domains: {
|
|
389
|
+
production: '${confirmedValues.productionUrl}',
|
|
390
|
+
staging: '${confirmedValues.stagingUrl}',
|
|
391
|
+
development: '${confirmedValues.developmentUrl}'
|
|
392
|
+
},
|
|
393
|
+
services: [
|
|
394
|
+
'${coreInputs.serviceName}'
|
|
395
|
+
],
|
|
396
|
+
databases: [
|
|
397
|
+
{
|
|
398
|
+
name: '${confirmedValues.databaseName}',
|
|
399
|
+
type: 'd1',
|
|
400
|
+
binding: 'DB'
|
|
401
|
+
}
|
|
402
|
+
],
|
|
403
|
+
features: ${JSON.stringify(confirmedValues.features, null, 4)},
|
|
404
|
+
metadata: {
|
|
405
|
+
version: '${confirmedValues.version}',
|
|
406
|
+
author: '${confirmedValues.author}',
|
|
407
|
+
generatedAt: '${new Date().toISOString()}',
|
|
408
|
+
frameworkVersion: '3.0.0',
|
|
409
|
+
serviceType: '${coreInputs.serviceType}',
|
|
410
|
+
environment: '${coreInputs.environment}'
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
export default domains;
|
|
416
|
+
`;
|
|
417
|
+
const filePath = join(servicePath, 'src', 'config', 'domains.js');
|
|
418
|
+
writeFileSync(filePath, domainsConfig, 'utf8');
|
|
419
|
+
return filePath;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Generate worker index.js
|
|
424
|
+
*/
|
|
425
|
+
generateWorkerIndex(coreInputs, confirmedValues, servicePath) {
|
|
426
|
+
const workerIndex = `/**
|
|
427
|
+
* ${confirmedValues.displayName} - Cloudflare Worker
|
|
428
|
+
*
|
|
429
|
+
* Generated by Clodo Framework GenerationEngine
|
|
430
|
+
* Service Type: ${coreInputs.serviceType}
|
|
431
|
+
*/
|
|
432
|
+
|
|
433
|
+
import { domains } from '../config/domains.js';
|
|
434
|
+
import { createServiceHandlers } from '../handlers/service-handlers.js';
|
|
435
|
+
import { createServiceMiddleware } from '../middleware/service-middleware.js';
|
|
436
|
+
|
|
437
|
+
export default {
|
|
438
|
+
async fetch(request, env, ctx) {
|
|
439
|
+
try {
|
|
440
|
+
// Get service configuration
|
|
441
|
+
const serviceConfig = domains['${coreInputs.serviceName}'];
|
|
442
|
+
|
|
443
|
+
// Apply middleware
|
|
444
|
+
const middleware = createServiceMiddleware(serviceConfig, env);
|
|
445
|
+
const processedRequest = await middleware.processRequest(request);
|
|
446
|
+
|
|
447
|
+
// Route to appropriate handler
|
|
448
|
+
const handlers = createServiceHandlers(serviceConfig, env);
|
|
449
|
+
const response = await handlers.handleRequest(processedRequest, ctx);
|
|
450
|
+
|
|
451
|
+
// Apply response middleware
|
|
452
|
+
return await middleware.processResponse(response);
|
|
453
|
+
|
|
454
|
+
} catch (error) {
|
|
455
|
+
console.error('Worker error:', error);
|
|
456
|
+
|
|
457
|
+
return new Response(JSON.stringify({
|
|
458
|
+
error: 'Internal Server Error',
|
|
459
|
+
message: error.message,
|
|
460
|
+
timestamp: new Date().toISOString()
|
|
461
|
+
}), {
|
|
462
|
+
status: 500,
|
|
463
|
+
headers: {
|
|
464
|
+
'Content-Type': 'application/json',
|
|
465
|
+
'X-Service': '${coreInputs.serviceName}',
|
|
466
|
+
'X-Version': '${confirmedValues.version}'
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
`;
|
|
473
|
+
const filePath = join(servicePath, 'src', 'worker', 'index.js');
|
|
474
|
+
writeFileSync(filePath, workerIndex, 'utf8');
|
|
475
|
+
return filePath;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Generate .env.example
|
|
480
|
+
*/
|
|
481
|
+
generateEnvExample(coreInputs, confirmedValues, servicePath) {
|
|
482
|
+
const envExample = `# ${confirmedValues.displayName} Environment Variables
|
|
483
|
+
# Generated by Clodo Framework GenerationEngine
|
|
484
|
+
|
|
485
|
+
# Cloudflare Configuration
|
|
486
|
+
CLOUDFLARE_ACCOUNT_ID=${coreInputs.cloudflareAccountId}
|
|
487
|
+
CLOUDFLARE_ZONE_ID=${coreInputs.cloudflareZoneId}
|
|
488
|
+
CLOUDFLARE_API_TOKEN=${coreInputs.cloudflareToken}
|
|
489
|
+
|
|
490
|
+
# Service Configuration
|
|
491
|
+
SERVICE_NAME=${coreInputs.serviceName}
|
|
492
|
+
SERVICE_TYPE=${coreInputs.serviceType}
|
|
493
|
+
DOMAIN_NAME=${coreInputs.domainName}
|
|
494
|
+
ENVIRONMENT=${coreInputs.environment}
|
|
495
|
+
|
|
496
|
+
# URLs
|
|
497
|
+
PRODUCTION_URL=${confirmedValues.productionUrl}
|
|
498
|
+
STAGING_URL=${confirmedValues.stagingUrl}
|
|
499
|
+
DEVELOPMENT_URL=${confirmedValues.developmentUrl}
|
|
500
|
+
DOCUMENTATION_URL=${confirmedValues.documentationUrl}
|
|
501
|
+
|
|
502
|
+
# API Configuration
|
|
503
|
+
API_BASE_PATH=${confirmedValues.apiBasePath}
|
|
504
|
+
HEALTH_CHECK_PATH=${confirmedValues.healthCheckPath}
|
|
505
|
+
|
|
506
|
+
# Database Configuration
|
|
507
|
+
DATABASE_NAME=${confirmedValues.databaseName}
|
|
508
|
+
|
|
509
|
+
# Feature Flags
|
|
510
|
+
${Object.entries(confirmedValues.features).filter(([, enabled]) => enabled).map(([feature]) => `FEATURE_${feature.toUpperCase()}=true`).join('\n')}
|
|
511
|
+
|
|
512
|
+
# Custom environment variables (uncomment and configure as needed)
|
|
513
|
+
# CUSTOM_VAR=value
|
|
514
|
+
# API_KEY=your-api-key-here
|
|
515
|
+
# SECRET_KEY=your-secret-key-here
|
|
516
|
+
`;
|
|
517
|
+
const filePath = join(servicePath, '.env.example');
|
|
518
|
+
writeFileSync(filePath, envExample, 'utf8');
|
|
519
|
+
return filePath;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Generate service schema
|
|
524
|
+
*/
|
|
525
|
+
generateServiceSchema(coreInputs, confirmedValues, servicePath) {
|
|
526
|
+
const schemaContent = `/**
|
|
527
|
+
* ${confirmedValues.displayName} - Service Schema
|
|
528
|
+
*
|
|
529
|
+
* Generated by Clodo Framework GenerationEngine
|
|
530
|
+
* Service Type: ${coreInputs.serviceType}
|
|
531
|
+
*/
|
|
532
|
+
|
|
533
|
+
import { z } from 'zod';
|
|
534
|
+
|
|
535
|
+
// Base service schema
|
|
536
|
+
export const baseServiceSchema = z.object({
|
|
537
|
+
id: z.string().uuid(),
|
|
538
|
+
createdAt: z.date(),
|
|
539
|
+
updatedAt: z.date(),
|
|
540
|
+
version: z.string().default('${confirmedValues.version}')
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
// Service-specific schemas based on type
|
|
544
|
+
${this.generateServiceTypeSchemas(coreInputs.serviceType)}
|
|
545
|
+
|
|
546
|
+
// Request/Response schemas
|
|
547
|
+
export const healthCheckResponseSchema = z.object({
|
|
548
|
+
status: z.enum(['healthy', 'unhealthy']),
|
|
549
|
+
timestamp: z.string().datetime(),
|
|
550
|
+
service: z.string(),
|
|
551
|
+
version: z.string(),
|
|
552
|
+
environment: z.string()
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
export const errorResponseSchema = z.object({
|
|
556
|
+
error: z.string(),
|
|
557
|
+
message: z.string(),
|
|
558
|
+
timestamp: z.string().datetime(),
|
|
559
|
+
service: z.string(),
|
|
560
|
+
version: z.string()
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
// Validation helpers
|
|
564
|
+
export function validateServiceRequest(data, schema) {
|
|
565
|
+
try {
|
|
566
|
+
return { success: true, data: schema.parse(data) };
|
|
567
|
+
} catch (error) {
|
|
568
|
+
return {
|
|
569
|
+
success: false,
|
|
570
|
+
error: error.errors.map(err => ({
|
|
571
|
+
field: err.path.join('.'),
|
|
572
|
+
message: err.message
|
|
573
|
+
}))
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
export function createServiceResponse(data, schema) {
|
|
579
|
+
try {
|
|
580
|
+
return { success: true, data: schema.parse(data) };
|
|
581
|
+
} catch (error) {
|
|
582
|
+
throw new Error(\`Response validation failed: \${error.message}\`);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
`;
|
|
586
|
+
const filePath = join(servicePath, 'src', 'schemas', 'service-schema.js');
|
|
587
|
+
writeFileSync(filePath, schemaContent, 'utf8');
|
|
588
|
+
return filePath;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Generate service type specific schemas
|
|
593
|
+
*/
|
|
594
|
+
generateServiceTypeSchemas(serviceType) {
|
|
595
|
+
const schemas = {
|
|
596
|
+
'data-service': `
|
|
597
|
+
export const dataItemSchema = z.object({
|
|
598
|
+
id: z.string().uuid(),
|
|
599
|
+
name: z.string().min(1).max(100),
|
|
600
|
+
description: z.string().max(500).optional(),
|
|
601
|
+
data: z.record(z.any()),
|
|
602
|
+
tags: z.array(z.string()).optional(),
|
|
603
|
+
status: z.enum(['active', 'inactive', 'archived']).default('active')
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
export const dataQuerySchema = z.object({
|
|
607
|
+
limit: z.number().min(1).max(100).default(20),
|
|
608
|
+
offset: z.number().min(0).default(0),
|
|
609
|
+
search: z.string().optional(),
|
|
610
|
+
filters: z.record(z.any()).optional(),
|
|
611
|
+
sortBy: z.string().optional(),
|
|
612
|
+
sortOrder: z.enum(['asc', 'desc']).default('asc')
|
|
613
|
+
});`,
|
|
614
|
+
'auth-service': `
|
|
615
|
+
export const userSchema = z.object({
|
|
616
|
+
id: z.string().uuid(),
|
|
617
|
+
email: z.string().email(),
|
|
618
|
+
username: z.string().min(3).max(50),
|
|
619
|
+
displayName: z.string().min(1).max(100),
|
|
620
|
+
roles: z.array(z.string()),
|
|
621
|
+
isActive: z.boolean().default(true),
|
|
622
|
+
lastLogin: z.date().optional(),
|
|
623
|
+
emailVerified: z.boolean().default(false)
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
export const authTokenSchema = z.object({
|
|
627
|
+
token: z.string(),
|
|
628
|
+
type: z.enum(['access', 'refresh']),
|
|
629
|
+
expiresAt: z.date(),
|
|
630
|
+
userId: z.string().uuid(),
|
|
631
|
+
scopes: z.array(z.string())
|
|
632
|
+
});`,
|
|
633
|
+
'content-service': `
|
|
634
|
+
export const contentItemSchema = z.object({
|
|
635
|
+
id: z.string().uuid(),
|
|
636
|
+
title: z.string().min(1).max(200),
|
|
637
|
+
content: z.string(),
|
|
638
|
+
contentType: z.enum(['article', 'page', 'media', 'document']),
|
|
639
|
+
slug: z.string().min(1).max(100),
|
|
640
|
+
author: z.string(),
|
|
641
|
+
published: z.boolean().default(false),
|
|
642
|
+
publishedAt: z.date().optional(),
|
|
643
|
+
tags: z.array(z.string()),
|
|
644
|
+
metadata: z.record(z.any())
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
export const mediaAssetSchema = z.object({
|
|
648
|
+
id: z.string().uuid(),
|
|
649
|
+
filename: z.string(),
|
|
650
|
+
originalName: z.string(),
|
|
651
|
+
mimeType: z.string(),
|
|
652
|
+
size: z.number(),
|
|
653
|
+
url: z.string(),
|
|
654
|
+
thumbnailUrl: z.string().optional(),
|
|
655
|
+
altText: z.string().optional()
|
|
656
|
+
});`,
|
|
657
|
+
'api-gateway': `
|
|
658
|
+
export const apiRouteSchema = z.object({
|
|
659
|
+
path: z.string().regex(/^\\/.*/),
|
|
660
|
+
method: z.enum(['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS']),
|
|
661
|
+
targetService: z.string(),
|
|
662
|
+
targetPath: z.string(),
|
|
663
|
+
rateLimit: z.number().optional(),
|
|
664
|
+
authentication: z.boolean().default(false),
|
|
665
|
+
authorization: z.array(z.string()).optional()
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
export const apiMetricsSchema = z.object({
|
|
669
|
+
route: z.string(),
|
|
670
|
+
method: z.string(),
|
|
671
|
+
responseTime: z.number(),
|
|
672
|
+
statusCode: z.number(),
|
|
673
|
+
timestamp: z.date(),
|
|
674
|
+
userAgent: z.string().optional(),
|
|
675
|
+
ipAddress: z.string().optional()
|
|
676
|
+
});`,
|
|
677
|
+
'generic': `
|
|
678
|
+
export const genericItemSchema = z.object({
|
|
679
|
+
id: z.string().uuid(),
|
|
680
|
+
type: z.string(),
|
|
681
|
+
data: z.record(z.any()),
|
|
682
|
+
metadata: z.record(z.any()).optional()
|
|
683
|
+
});`
|
|
684
|
+
};
|
|
685
|
+
return schemas[serviceType] || schemas.generic;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* Generate service handlers
|
|
690
|
+
*/
|
|
691
|
+
generateServiceHandlers(coreInputs, confirmedValues, servicePath) {
|
|
692
|
+
const handlersContent = `/**
|
|
693
|
+
* ${confirmedValues.displayName} - Service Handlers
|
|
694
|
+
*
|
|
695
|
+
* Generated by Clodo Framework GenerationEngine
|
|
696
|
+
* Service Type: ${coreInputs.serviceType}
|
|
697
|
+
*/
|
|
698
|
+
|
|
699
|
+
import { healthCheckResponseSchema, errorResponseSchema } from '../schemas/service-schema.js';
|
|
700
|
+
|
|
701
|
+
export function createServiceHandlers(serviceConfig, env) {
|
|
702
|
+
return {
|
|
703
|
+
async handleRequest(request, ctx) {
|
|
704
|
+
const url = new URL(request.url);
|
|
705
|
+
const path = url.pathname;
|
|
706
|
+
|
|
707
|
+
// Health check endpoint
|
|
708
|
+
if (path === '${confirmedValues.healthCheckPath}') {
|
|
709
|
+
return this.handleHealthCheck(request, serviceConfig);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
// API routes
|
|
713
|
+
if (path.startsWith('${confirmedValues.apiBasePath}')) {
|
|
714
|
+
return this.handleApiRequest(request, ctx, serviceConfig, env);
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
// Default 404 response
|
|
718
|
+
return new Response(JSON.stringify({
|
|
719
|
+
error: 'Not Found',
|
|
720
|
+
message: \`Endpoint not found: \${path}\`,
|
|
721
|
+
availableEndpoints: [
|
|
722
|
+
'${confirmedValues.healthCheckPath}',
|
|
723
|
+
'${confirmedValues.apiBasePath}/*'
|
|
724
|
+
]
|
|
725
|
+
}), {
|
|
726
|
+
status: 404,
|
|
727
|
+
headers: { 'Content-Type': 'application/json' }
|
|
728
|
+
});
|
|
729
|
+
},
|
|
730
|
+
|
|
731
|
+
async handleHealthCheck(request, serviceConfig) {
|
|
732
|
+
try {
|
|
733
|
+
// Perform health checks
|
|
734
|
+
const healthStatus = await this.performHealthChecks(serviceConfig, env);
|
|
735
|
+
|
|
736
|
+
const response = {
|
|
737
|
+
status: healthStatus.overall === 'healthy' ? 'healthy' : 'unhealthy',
|
|
738
|
+
timestamp: new Date().toISOString(),
|
|
739
|
+
service: serviceConfig.name,
|
|
740
|
+
version: '${confirmedValues.version}',
|
|
741
|
+
environment: '${coreInputs.environment}',
|
|
742
|
+
checks: healthStatus.checks
|
|
743
|
+
};
|
|
744
|
+
|
|
745
|
+
return new Response(JSON.stringify(response), {
|
|
746
|
+
status: healthStatus.overall === 'healthy' ? 200 : 503,
|
|
747
|
+
headers: { 'Content-Type': 'application/json' }
|
|
748
|
+
});
|
|
749
|
+
} catch (error) {
|
|
750
|
+
return new Response(JSON.stringify({
|
|
751
|
+
status: 'unhealthy',
|
|
752
|
+
timestamp: new Date().toISOString(),
|
|
753
|
+
service: serviceConfig.name,
|
|
754
|
+
error: error.message
|
|
755
|
+
}), {
|
|
756
|
+
status: 503,
|
|
757
|
+
headers: { 'Content-Type': 'application/json' }
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
},
|
|
761
|
+
|
|
762
|
+
async handleApiRequest(request, ctx, serviceConfig, env) {
|
|
763
|
+
try {
|
|
764
|
+
const url = new URL(request.url);
|
|
765
|
+
const path = url.pathname.replace('${confirmedValues.apiBasePath}', '');
|
|
766
|
+
|
|
767
|
+
// Route to service-specific handlers
|
|
768
|
+
switch (request.method) {
|
|
769
|
+
case 'GET':
|
|
770
|
+
return this.handleGet(path, request, ctx, serviceConfig, env);
|
|
771
|
+
case 'POST':
|
|
772
|
+
return this.handlePost(path, request, ctx, serviceConfig, env);
|
|
773
|
+
case 'PUT':
|
|
774
|
+
return this.handlePut(path, request, ctx, serviceConfig, env);
|
|
775
|
+
case 'DELETE':
|
|
776
|
+
return this.handleDelete(path, request, ctx, serviceConfig, env);
|
|
777
|
+
default:
|
|
778
|
+
return new Response(JSON.stringify({
|
|
779
|
+
error: 'Method Not Allowed',
|
|
780
|
+
message: \`Method \${request.method} not supported\`
|
|
781
|
+
}), {
|
|
782
|
+
status: 405,
|
|
783
|
+
headers: { 'Content-Type': 'application/json' }
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
} catch (error) {
|
|
787
|
+
return new Response(JSON.stringify({
|
|
788
|
+
error: 'Internal Server Error',
|
|
789
|
+
message: error.message,
|
|
790
|
+
timestamp: new Date().toISOString()
|
|
791
|
+
}), {
|
|
792
|
+
status: 500,
|
|
793
|
+
headers: { 'Content-Type': 'application/json' }
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
},
|
|
797
|
+
|
|
798
|
+
async performHealthChecks(serviceConfig, env) {
|
|
799
|
+
const checks = [];
|
|
800
|
+
|
|
801
|
+
// Database connectivity check
|
|
802
|
+
try {
|
|
803
|
+
${confirmedValues.features.database ? `
|
|
804
|
+
// Check database connection
|
|
805
|
+
const dbCheck = await env.DB.prepare('SELECT 1 as health_check').first();
|
|
806
|
+
checks.push({
|
|
807
|
+
name: 'database',
|
|
808
|
+
status: dbCheck ? 'healthy' : 'unhealthy',
|
|
809
|
+
responseTime: Date.now()
|
|
810
|
+
});` : `
|
|
811
|
+
checks.push({
|
|
812
|
+
name: 'database',
|
|
813
|
+
status: 'disabled',
|
|
814
|
+
message: 'Database not configured for this service type'
|
|
815
|
+
});`}
|
|
816
|
+
} catch (error) {
|
|
817
|
+
checks.push({
|
|
818
|
+
name: 'database',
|
|
819
|
+
status: 'unhealthy',
|
|
820
|
+
error: error.message
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
// Service configuration check
|
|
825
|
+
checks.push({
|
|
826
|
+
name: 'configuration',
|
|
827
|
+
status: serviceConfig ? 'healthy' : 'unhealthy',
|
|
828
|
+
message: serviceConfig ? 'Service configuration loaded' : 'Service configuration missing'
|
|
829
|
+
});
|
|
830
|
+
|
|
831
|
+
// Overall health status
|
|
832
|
+
const overall = checks.every(check => check.status === 'healthy' || check.status === 'disabled')
|
|
833
|
+
? 'healthy' : 'unhealthy';
|
|
834
|
+
|
|
835
|
+
return { overall, checks };
|
|
836
|
+
},
|
|
837
|
+
|
|
838
|
+
// Placeholder handlers - implement based on service type
|
|
839
|
+
async handleGet(path, request, ctx, serviceConfig, env) {
|
|
840
|
+
return new Response(JSON.stringify({
|
|
841
|
+
message: 'GET handler not implemented',
|
|
842
|
+
path,
|
|
843
|
+
service: serviceConfig.name,
|
|
844
|
+
timestamp: new Date().toISOString()
|
|
845
|
+
}), {
|
|
846
|
+
headers: { 'Content-Type': 'application/json' }
|
|
847
|
+
});
|
|
848
|
+
},
|
|
849
|
+
|
|
850
|
+
async handlePost(path, request, ctx, serviceConfig, env) {
|
|
851
|
+
return new Response(JSON.stringify({
|
|
852
|
+
message: 'POST handler not implemented',
|
|
853
|
+
path,
|
|
854
|
+
service: serviceConfig.name,
|
|
855
|
+
timestamp: new Date().toISOString()
|
|
856
|
+
}), {
|
|
857
|
+
headers: { 'Content-Type': 'application/json' }
|
|
858
|
+
});
|
|
859
|
+
},
|
|
860
|
+
|
|
861
|
+
async handlePut(path, request, ctx, serviceConfig, env) {
|
|
862
|
+
return new Response(JSON.stringify({
|
|
863
|
+
message: 'PUT handler not implemented',
|
|
864
|
+
path,
|
|
865
|
+
service: serviceConfig.name,
|
|
866
|
+
timestamp: new Date().toISOString()
|
|
867
|
+
}), {
|
|
868
|
+
headers: { 'Content-Type': 'application/json' }
|
|
869
|
+
});
|
|
870
|
+
},
|
|
871
|
+
|
|
872
|
+
async handleDelete(path, request, ctx, serviceConfig, env) {
|
|
873
|
+
return new Response(JSON.stringify({
|
|
874
|
+
message: 'DELETE handler not implemented',
|
|
875
|
+
path,
|
|
876
|
+
service: serviceConfig.name,
|
|
877
|
+
timestamp: new Date().toISOString()
|
|
878
|
+
}), {
|
|
879
|
+
headers: { 'Content-Type': 'application/json' }
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
`;
|
|
885
|
+
const filePath = join(servicePath, 'src', 'handlers', 'service-handlers.js');
|
|
886
|
+
writeFileSync(filePath, handlersContent, 'utf8');
|
|
887
|
+
return filePath;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
/**
|
|
891
|
+
* Generate service middleware
|
|
892
|
+
*/
|
|
893
|
+
generateServiceMiddleware(coreInputs, confirmedValues, servicePath) {
|
|
894
|
+
const middlewareContent = `/**
|
|
895
|
+
* ${confirmedValues.displayName} - Service Middleware
|
|
896
|
+
*
|
|
897
|
+
* Generated by Clodo Framework GenerationEngine
|
|
898
|
+
* Service Type: ${coreInputs.serviceType}
|
|
899
|
+
*/
|
|
900
|
+
|
|
901
|
+
export function createServiceMiddleware(serviceConfig, env) {
|
|
902
|
+
return {
|
|
903
|
+
async processRequest(request) {
|
|
904
|
+
let processedRequest = request;
|
|
905
|
+
|
|
906
|
+
// Add service context headers
|
|
907
|
+
const headers = new Headers(request.headers);
|
|
908
|
+
headers.set('X-Service', serviceConfig.name);
|
|
909
|
+
headers.set('X-Version', '${confirmedValues.version}');
|
|
910
|
+
headers.set('X-Environment', '${coreInputs.environment}');
|
|
911
|
+
headers.set('X-Request-ID', crypto.randomUUID());
|
|
912
|
+
|
|
913
|
+
// CORS headers for API requests
|
|
914
|
+
if (request.url.includes('${confirmedValues.apiBasePath}')) {
|
|
915
|
+
headers.set('Access-Control-Allow-Origin', '*');
|
|
916
|
+
headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
|
917
|
+
headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
${confirmedValues.features.logging ? `
|
|
921
|
+
// Request logging
|
|
922
|
+
console.log(\`[\${new Date().toISOString()}] \${request.method} \${request.url}\`);` : ''}
|
|
923
|
+
|
|
924
|
+
${confirmedValues.features.rateLimiting ? `
|
|
925
|
+
// Rate limiting (placeholder - implement based on requirements)
|
|
926
|
+
// This would typically check request frequency and block if over limit` : ''}
|
|
927
|
+
|
|
928
|
+
${confirmedValues.features.authentication ? `
|
|
929
|
+
// Authentication middleware (placeholder)
|
|
930
|
+
// This would validate JWT tokens, API keys, etc.` : ''}
|
|
931
|
+
|
|
932
|
+
${confirmedValues.features.authorization ? `
|
|
933
|
+
// Authorization middleware (placeholder)
|
|
934
|
+
// This would check user permissions and roles` : ''}
|
|
935
|
+
|
|
936
|
+
return new Request(request.url, {
|
|
937
|
+
...request,
|
|
938
|
+
headers
|
|
939
|
+
});
|
|
940
|
+
},
|
|
941
|
+
|
|
942
|
+
async processResponse(response) {
|
|
943
|
+
const headers = new Headers(response.headers);
|
|
944
|
+
|
|
945
|
+
// Add standard response headers
|
|
946
|
+
headers.set('X-Service', serviceConfig.name);
|
|
947
|
+
headers.set('X-Version', '${confirmedValues.version}');
|
|
948
|
+
headers.set('X-Response-Time', Date.now().toString());
|
|
949
|
+
|
|
950
|
+
${confirmedValues.features.monitoring ? `
|
|
951
|
+
// Response monitoring
|
|
952
|
+
console.log(\`Response: \${response.status} (\${Date.now()}ms)\`);` : ''}
|
|
953
|
+
|
|
954
|
+
${confirmedValues.features.caching ? `
|
|
955
|
+
// Cache headers (placeholder - implement based on content type)
|
|
956
|
+
if (response.status === 200) {
|
|
957
|
+
headers.set('Cache-Control', 'public, max-age=300'); // 5 minutes
|
|
958
|
+
}` : ''}
|
|
959
|
+
|
|
960
|
+
return new Response(response.body, {
|
|
961
|
+
...response,
|
|
962
|
+
headers
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
`;
|
|
968
|
+
const filePath = join(servicePath, 'src', 'middleware', 'service-middleware.js');
|
|
969
|
+
writeFileSync(filePath, middlewareContent, 'utf8');
|
|
970
|
+
return filePath;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
/**
|
|
974
|
+
* Generate service utilities
|
|
975
|
+
*/
|
|
976
|
+
generateServiceUtils(coreInputs, confirmedValues, servicePath) {
|
|
977
|
+
const utilsContent = `/**
|
|
978
|
+
* ${confirmedValues.displayName} - Service Utilities
|
|
979
|
+
*
|
|
980
|
+
* Generated by Clodo Framework GenerationEngine
|
|
981
|
+
* Service Type: ${coreInputs.serviceType}
|
|
982
|
+
*/
|
|
983
|
+
|
|
984
|
+
/**
|
|
985
|
+
* Utility functions for ${confirmedValues.displayName}
|
|
986
|
+
*/
|
|
987
|
+
|
|
988
|
+
// Database utilities
|
|
989
|
+
export class DatabaseUtils {
|
|
990
|
+
static async executeQuery(env, query, params = []) {
|
|
991
|
+
try {
|
|
992
|
+
const stmt = env.DB.prepare(query);
|
|
993
|
+
if (params.length > 0) {
|
|
994
|
+
return await stmt.bind(...params).all();
|
|
995
|
+
}
|
|
996
|
+
return await stmt.all();
|
|
997
|
+
} catch (error) {
|
|
998
|
+
console.error('Database query error:', error);
|
|
999
|
+
throw new Error(\`Database operation failed: \${error.message}\`);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
static async getById(env, table, id) {
|
|
1004
|
+
const result = await this.executeQuery(
|
|
1005
|
+
env,
|
|
1006
|
+
\`SELECT * FROM \${table} WHERE id = ?\`,
|
|
1007
|
+
[id]
|
|
1008
|
+
);
|
|
1009
|
+
return result.results[0] || null;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
static async create(env, table, data) {
|
|
1013
|
+
const columns = Object.keys(data).join(', ');
|
|
1014
|
+
const placeholders = Object.keys(data).map(() => '?').join(', ');
|
|
1015
|
+
const values = Object.values(data);
|
|
1016
|
+
|
|
1017
|
+
const result = await this.executeQuery(
|
|
1018
|
+
env,
|
|
1019
|
+
\`INSERT INTO \${table} (\${columns}) VALUES (\${placeholders})\`,
|
|
1020
|
+
values
|
|
1021
|
+
);
|
|
1022
|
+
|
|
1023
|
+
return result.meta.last_row_id;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
static async update(env, table, id, data) {
|
|
1027
|
+
const setClause = Object.keys(data).map(key => \`\${key} = ?\`).join(', ');
|
|
1028
|
+
const values = [...Object.values(data), id];
|
|
1029
|
+
|
|
1030
|
+
const result = await this.executeQuery(
|
|
1031
|
+
env,
|
|
1032
|
+
\`UPDATE \${table} SET \${setClause} WHERE id = ?\`,
|
|
1033
|
+
values
|
|
1034
|
+
);
|
|
1035
|
+
|
|
1036
|
+
return result.meta.changes > 0;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
static async delete(env, table, id) {
|
|
1040
|
+
const result = await this.executeQuery(
|
|
1041
|
+
env,
|
|
1042
|
+
\`DELETE FROM \${table} WHERE id = ?\`,
|
|
1043
|
+
[id]
|
|
1044
|
+
);
|
|
1045
|
+
|
|
1046
|
+
return result.meta.changes > 0;
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
// Validation utilities
|
|
1051
|
+
export class ValidationUtils {
|
|
1052
|
+
static isValidUUID(uuid) {
|
|
1053
|
+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
1054
|
+
return uuidRegex.test(uuid);
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
static isValidEmail(email) {
|
|
1058
|
+
const emailRegex = /^[^@]+@[^@]+.[^@]+$/;
|
|
1059
|
+
return emailRegex.test(email);
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
static sanitizeString(str, maxLength = 1000) {
|
|
1063
|
+
if (typeof str !== 'string') return '';
|
|
1064
|
+
return str.trim().substring(0, maxLength);
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
static validatePagination(limit = 20, offset = 0) {
|
|
1068
|
+
const maxLimit = 100;
|
|
1069
|
+
const sanitizedLimit = Math.min(Math.max(1, limit), maxLimit);
|
|
1070
|
+
const sanitizedOffset = Math.max(0, offset);
|
|
1071
|
+
|
|
1072
|
+
return { limit: sanitizedLimit, offset: sanitizedOffset };
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
// Response utilities
|
|
1077
|
+
export class ResponseUtils {
|
|
1078
|
+
static createSuccessResponse(data, status = 200) {
|
|
1079
|
+
return new Response(JSON.stringify({
|
|
1080
|
+
success: true,
|
|
1081
|
+
data,
|
|
1082
|
+
timestamp: new Date().toISOString()
|
|
1083
|
+
}), {
|
|
1084
|
+
status,
|
|
1085
|
+
headers: { 'Content-Type': 'application/json' }
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
static createErrorResponse(error, status = 500, details = null) {
|
|
1090
|
+
const errorResponse = {
|
|
1091
|
+
success: false,
|
|
1092
|
+
error: error.message || 'Internal Server Error',
|
|
1093
|
+
timestamp: new Date().toISOString()
|
|
1094
|
+
};
|
|
1095
|
+
|
|
1096
|
+
if (details) {
|
|
1097
|
+
errorResponse.details = details;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
return new Response(JSON.stringify(errorResponse), {
|
|
1101
|
+
status,
|
|
1102
|
+
headers: { 'Content-Type': 'application/json' }
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
static createPaginatedResponse(data, pagination, status = 200) {
|
|
1107
|
+
return new Response(JSON.stringify({
|
|
1108
|
+
success: true,
|
|
1109
|
+
data,
|
|
1110
|
+
pagination: {
|
|
1111
|
+
...pagination,
|
|
1112
|
+
hasMore: data.length === pagination.limit
|
|
1113
|
+
},
|
|
1114
|
+
timestamp: new Date().toISOString()
|
|
1115
|
+
}), {
|
|
1116
|
+
status,
|
|
1117
|
+
headers: { 'Content-Type': 'application/json' }
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
// Logging utilities
|
|
1123
|
+
export class LoggingUtils {
|
|
1124
|
+
static logRequest(request, context = {}) {
|
|
1125
|
+
console.log(JSON.stringify({
|
|
1126
|
+
timestamp: new Date().toISOString(),
|
|
1127
|
+
level: 'info',
|
|
1128
|
+
type: 'request',
|
|
1129
|
+
method: request.method,
|
|
1130
|
+
url: request.url,
|
|
1131
|
+
userAgent: request.headers.get('User-Agent'),
|
|
1132
|
+
...context
|
|
1133
|
+
}));
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
static logError(error, context = {}) {
|
|
1137
|
+
console.error(JSON.stringify({
|
|
1138
|
+
timestamp: new Date().toISOString(),
|
|
1139
|
+
level: 'error',
|
|
1140
|
+
type: 'error',
|
|
1141
|
+
message: error.message,
|
|
1142
|
+
stack: error.stack,
|
|
1143
|
+
...context
|
|
1144
|
+
}));
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
static logPerformance(operation, startTime, context = {}) {
|
|
1148
|
+
const duration = Date.now() - startTime;
|
|
1149
|
+
console.log(JSON.stringify({
|
|
1150
|
+
timestamp: new Date().toISOString(),
|
|
1151
|
+
level: 'info',
|
|
1152
|
+
type: 'performance',
|
|
1153
|
+
operation,
|
|
1154
|
+
duration,
|
|
1155
|
+
...context
|
|
1156
|
+
}));
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
// Feature flag utilities
|
|
1161
|
+
export class FeatureUtils {
|
|
1162
|
+
static isFeatureEnabled(featureName, serviceConfig) {
|
|
1163
|
+
return serviceConfig.features && serviceConfig.features[featureName] === true;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
static getEnabledFeatures(serviceConfig) {
|
|
1167
|
+
if (!serviceConfig.features) return [];
|
|
1168
|
+
return Object.entries(serviceConfig.features)
|
|
1169
|
+
.filter(([, enabled]) => enabled)
|
|
1170
|
+
.map(([feature]) => feature);
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
static getDisabledFeatures(serviceConfig) {
|
|
1174
|
+
if (!serviceConfig.features) return [];
|
|
1175
|
+
return Object.entries(serviceConfig.features)
|
|
1176
|
+
.filter(([, enabled]) => !enabled)
|
|
1177
|
+
.map(([feature]) => feature);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
`;
|
|
1181
|
+
const filePath = join(servicePath, 'src', 'utils', 'service-utils.js');
|
|
1182
|
+
writeFileSync(filePath, utilsContent, 'utf8');
|
|
1183
|
+
return filePath;
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
/**
|
|
1187
|
+
* Generate deployment script
|
|
1188
|
+
*/
|
|
1189
|
+
generateDeployScript(coreInputs, confirmedValues, servicePath) {
|
|
1190
|
+
const deployScript = `#!/usr/bin/env pwsh
|
|
1191
|
+
|
|
1192
|
+
<#
|
|
1193
|
+
.SYNOPSIS
|
|
1194
|
+
Deploy ${confirmedValues.displayName} to Cloudflare
|
|
1195
|
+
|
|
1196
|
+
.DESCRIPTION
|
|
1197
|
+
Automated deployment script for ${confirmedValues.displayName}
|
|
1198
|
+
Handles database setup, worker deployment, and environment configuration
|
|
1199
|
+
|
|
1200
|
+
.PARAMETER Environment
|
|
1201
|
+
Target environment (development, staging, production)
|
|
1202
|
+
|
|
1203
|
+
.PARAMETER SkipTests
|
|
1204
|
+
Skip running tests before deployment
|
|
1205
|
+
|
|
1206
|
+
.EXAMPLE
|
|
1207
|
+
.\\scripts\\deploy.ps1 -Environment production
|
|
1208
|
+
|
|
1209
|
+
.EXAMPLE
|
|
1210
|
+
.\\scripts\\deploy.ps1 -Environment staging -SkipTests
|
|
1211
|
+
#>
|
|
1212
|
+
|
|
1213
|
+
param(
|
|
1214
|
+
[Parameter(Mandatory = $false)]
|
|
1215
|
+
[ValidateSet('development', 'staging', 'production')]
|
|
1216
|
+
[string]$Environment = 'development',
|
|
1217
|
+
|
|
1218
|
+
[Parameter(Mandatory = $false)]
|
|
1219
|
+
[switch]$SkipTests
|
|
1220
|
+
)
|
|
1221
|
+
|
|
1222
|
+
Write-Host "🚀 Deploying ${confirmedValues.displayName} to $Environment" -ForegroundColor Cyan
|
|
1223
|
+
|
|
1224
|
+
# Load environment variables
|
|
1225
|
+
if (Test-Path ".env") {
|
|
1226
|
+
Write-Host "📄 Loading environment variables from .env" -ForegroundColor Gray
|
|
1227
|
+
Get-Content ".env" | ForEach-Object {
|
|
1228
|
+
if ($_ -match '^([^=]+)=(.*)$') {
|
|
1229
|
+
$key = $matches[1]
|
|
1230
|
+
$value = $matches[2]
|
|
1231
|
+
[Environment]::SetEnvironmentVariable($key, $value)
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
try {
|
|
1237
|
+
# Run tests unless skipped
|
|
1238
|
+
if (-not $SkipTests) {
|
|
1239
|
+
Write-Host "🧪 Running tests..." -ForegroundColor Yellow
|
|
1240
|
+
npm test
|
|
1241
|
+
if ($LASTEXITCODE -ne 0) {
|
|
1242
|
+
throw "Tests failed. Aborting deployment."
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
# Lint code
|
|
1247
|
+
Write-Host "🔍 Running linter..." -ForegroundColor Yellow
|
|
1248
|
+
npm run lint
|
|
1249
|
+
if ($LASTEXITCODE -ne 0) {
|
|
1250
|
+
throw "Linting failed. Aborting deployment."
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
# Deploy to Cloudflare
|
|
1254
|
+
Write-Host "☁️ Deploying to Cloudflare Workers..." -ForegroundColor Yellow
|
|
1255
|
+
npx wrangler deploy --env $Environment
|
|
1256
|
+
|
|
1257
|
+
if ($LASTEXITCODE -ne 0) {
|
|
1258
|
+
throw "Cloudflare deployment failed."
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
# Run health check
|
|
1262
|
+
Write-Host "🏥 Running health check..." -ForegroundColor Yellow
|
|
1263
|
+
.\\scripts\\health-check.ps1 -Environment $Environment
|
|
1264
|
+
|
|
1265
|
+
Write-Host "✅ Deployment completed successfully!" -ForegroundColor Green
|
|
1266
|
+
Write-Host "🌐 Service URL: ${confirmedValues.productionUrl}" -ForegroundColor Cyan
|
|
1267
|
+
|
|
1268
|
+
} catch {
|
|
1269
|
+
Write-Host "❌ Deployment failed: $_" -ForegroundColor Red
|
|
1270
|
+
exit 1
|
|
1271
|
+
}
|
|
1272
|
+
`;
|
|
1273
|
+
const filePath = join(servicePath, 'scripts', 'deploy.ps1');
|
|
1274
|
+
writeFileSync(filePath, deployScript, 'utf8');
|
|
1275
|
+
return filePath;
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
/**
|
|
1279
|
+
* Generate setup script
|
|
1280
|
+
*/
|
|
1281
|
+
generateSetupScript(coreInputs, confirmedValues, servicePath) {
|
|
1282
|
+
const setupScript = `#!/usr/bin/env pwsh
|
|
1283
|
+
|
|
1284
|
+
<#
|
|
1285
|
+
.SYNOPSIS
|
|
1286
|
+
Setup ${confirmedValues.displayName} environment
|
|
1287
|
+
|
|
1288
|
+
.DESCRIPTION
|
|
1289
|
+
Initializes the development environment for ${confirmedValues.displayName}
|
|
1290
|
+
Sets up database, configures environment variables, and prepares for development
|
|
1291
|
+
|
|
1292
|
+
.EXAMPLE
|
|
1293
|
+
.\\scripts\\setup.ps1
|
|
1294
|
+
#>
|
|
1295
|
+
|
|
1296
|
+
Write-Host "🔧 Setting up ${confirmedValues.displayName} development environment" -ForegroundColor Cyan
|
|
1297
|
+
|
|
1298
|
+
try {
|
|
1299
|
+
# Check if .env exists
|
|
1300
|
+
if (-not (Test-Path ".env")) {
|
|
1301
|
+
Write-Host "📄 Creating .env file from template..." -ForegroundColor Yellow
|
|
1302
|
+
Copy-Item ".env.example" ".env" -Force
|
|
1303
|
+
Write-Host "⚠️ Please edit .env file with your actual values" -ForegroundColor Yellow
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
# Install dependencies
|
|
1307
|
+
Write-Host "📦 Installing dependencies..." -ForegroundColor Yellow
|
|
1308
|
+
npm install
|
|
1309
|
+
|
|
1310
|
+
# Create database (if configured)
|
|
1311
|
+
${confirmedValues.features.database ? `
|
|
1312
|
+
Write-Host "🗄️ Setting up database..." -ForegroundColor Yellow
|
|
1313
|
+
# Database setup would go here
|
|
1314
|
+
Write-Host "⚠️ Database setup requires manual configuration" -ForegroundColor Yellow
|
|
1315
|
+
` : `
|
|
1316
|
+
Write-Host "ℹ️ Database not required for this service type" -ForegroundColor Gray
|
|
1317
|
+
`}
|
|
1318
|
+
|
|
1319
|
+
# Run initial build
|
|
1320
|
+
Write-Host "🔨 Running initial build..." -ForegroundColor Yellow
|
|
1321
|
+
npm run build
|
|
1322
|
+
|
|
1323
|
+
# Run tests
|
|
1324
|
+
Write-Host "🧪 Running tests..." -ForegroundColor Yellow
|
|
1325
|
+
npm test
|
|
1326
|
+
|
|
1327
|
+
Write-Host "✅ Setup completed successfully!" -ForegroundColor Green
|
|
1328
|
+
Write-Host "🚀 You can now run 'npm run dev' to start development" -ForegroundColor Cyan
|
|
1329
|
+
|
|
1330
|
+
} catch {
|
|
1331
|
+
Write-Host "❌ Setup failed: $_" -ForegroundColor Red
|
|
1332
|
+
exit 1
|
|
1333
|
+
}
|
|
1334
|
+
`;
|
|
1335
|
+
const filePath = join(servicePath, 'scripts', 'setup.ps1');
|
|
1336
|
+
writeFileSync(filePath, setupScript, 'utf8');
|
|
1337
|
+
return filePath;
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
/**
|
|
1341
|
+
* Generate health check script
|
|
1342
|
+
*/
|
|
1343
|
+
generateHealthCheckScript(coreInputs, confirmedValues, servicePath) {
|
|
1344
|
+
const healthCheckScript = `#!/usr/bin/env pwsh
|
|
1345
|
+
|
|
1346
|
+
<#
|
|
1347
|
+
.SYNOPSIS
|
|
1348
|
+
Health check for ${confirmedValues.displayName}
|
|
1349
|
+
|
|
1350
|
+
.DESCRIPTION
|
|
1351
|
+
Performs health checks on ${confirmedValues.displayName} service
|
|
1352
|
+
Tests endpoints, database connectivity, and overall service health
|
|
1353
|
+
|
|
1354
|
+
.PARAMETER Environment
|
|
1355
|
+
Target environment to check (development, staging, production)
|
|
1356
|
+
|
|
1357
|
+
.EXAMPLE
|
|
1358
|
+
.\\scripts\\health-check.ps1 -Environment production
|
|
1359
|
+
#>
|
|
1360
|
+
|
|
1361
|
+
param(
|
|
1362
|
+
[Parameter(Mandatory = $false)]
|
|
1363
|
+
[ValidateSet('development', 'staging', 'production')]
|
|
1364
|
+
[string]$Environment = 'development'
|
|
1365
|
+
)
|
|
1366
|
+
|
|
1367
|
+
Write-Host "🏥 Running health checks for ${confirmedValues.displayName} ($Environment)" -ForegroundColor Cyan
|
|
1368
|
+
|
|
1369
|
+
# Determine service URL based on environment
|
|
1370
|
+
$serviceUrl = switch ($Environment) {
|
|
1371
|
+
'production' { "${confirmedValues.productionUrl}" }
|
|
1372
|
+
'staging' { "${confirmedValues.stagingUrl}" }
|
|
1373
|
+
'development' { "${confirmedValues.developmentUrl}" }
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
Write-Host "🌐 Checking service at: $serviceUrl" -ForegroundColor Gray
|
|
1377
|
+
|
|
1378
|
+
try {
|
|
1379
|
+
# Health check endpoint
|
|
1380
|
+
$healthUrl = "$serviceUrl${confirmedValues.healthCheckPath}"
|
|
1381
|
+
Write-Host "🔍 Testing health endpoint: $healthUrl" -ForegroundColor Yellow
|
|
1382
|
+
|
|
1383
|
+
$response = Invoke-RestMethod -Uri $healthUrl -Method GET -TimeoutSec 30
|
|
1384
|
+
if ($response.status -eq 'healthy') {
|
|
1385
|
+
Write-Host "✅ Health check passed" -ForegroundColor Green
|
|
1386
|
+
} else {
|
|
1387
|
+
Write-Host "❌ Health check failed: $($response | ConvertTo-Json)" -ForegroundColor Red
|
|
1388
|
+
exit 1
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
# API endpoint check
|
|
1392
|
+
$apiUrl = "$serviceUrl${confirmedValues.apiBasePath}"
|
|
1393
|
+
Write-Host "🔍 Testing API endpoint: $apiUrl" -ForegroundColor Yellow
|
|
1394
|
+
|
|
1395
|
+
try {
|
|
1396
|
+
$apiResponse = Invoke-WebRequest -Uri $apiUrl -Method GET -TimeoutSec 30
|
|
1397
|
+
Write-Host "✅ API endpoint accessible (Status: $($apiResponse.StatusCode))" -ForegroundColor Green
|
|
1398
|
+
} catch {
|
|
1399
|
+
Write-Host "⚠️ API endpoint returned error (may be expected): $($_.Exception.Message)" -ForegroundColor Yellow
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
Write-Host "✅ All health checks completed successfully!" -ForegroundColor Green
|
|
1403
|
+
|
|
1404
|
+
} catch {
|
|
1405
|
+
Write-Host "❌ Health check failed: $_" -ForegroundColor Red
|
|
1406
|
+
exit 1
|
|
1407
|
+
}
|
|
1408
|
+
`;
|
|
1409
|
+
const filePath = join(servicePath, 'scripts', 'health-check.ps1');
|
|
1410
|
+
writeFileSync(filePath, healthCheckScript, 'utf8');
|
|
1411
|
+
return filePath;
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
/**
|
|
1415
|
+
* Generate environment configuration files
|
|
1416
|
+
*/
|
|
1417
|
+
generateProductionEnv(coreInputs, confirmedValues, servicePath) {
|
|
1418
|
+
const envContent = `# Production Environment Configuration
|
|
1419
|
+
# Generated by Clodo Framework GenerationEngine
|
|
1420
|
+
|
|
1421
|
+
# Service Configuration
|
|
1422
|
+
SERVICE_NAME=${coreInputs.serviceName}
|
|
1423
|
+
SERVICE_TYPE=${coreInputs.serviceType}
|
|
1424
|
+
ENVIRONMENT=production
|
|
1425
|
+
|
|
1426
|
+
# URLs
|
|
1427
|
+
PRODUCTION_URL=${confirmedValues.productionUrl}
|
|
1428
|
+
STAGING_URL=${confirmedValues.stagingUrl}
|
|
1429
|
+
DEVELOPMENT_URL=${confirmedValues.developmentUrl}
|
|
1430
|
+
|
|
1431
|
+
# Cloudflare Configuration
|
|
1432
|
+
CLOUDFLARE_ACCOUNT_ID=${coreInputs.cloudflareAccountId}
|
|
1433
|
+
CLOUDFLARE_ZONE_ID=${coreInputs.cloudflareZoneId}
|
|
1434
|
+
|
|
1435
|
+
# Database
|
|
1436
|
+
DATABASE_NAME=${confirmedValues.databaseName}
|
|
1437
|
+
|
|
1438
|
+
# Logging and Monitoring
|
|
1439
|
+
LOG_LEVEL=warn
|
|
1440
|
+
METRICS_ENABLED=true
|
|
1441
|
+
ERROR_REPORTING_ENABLED=true
|
|
1442
|
+
`;
|
|
1443
|
+
const filePath = join(servicePath, 'config', 'production.env');
|
|
1444
|
+
writeFileSync(filePath, envContent, 'utf8');
|
|
1445
|
+
return filePath;
|
|
1446
|
+
}
|
|
1447
|
+
generateStagingEnv(coreInputs, confirmedValues, servicePath) {
|
|
1448
|
+
const envContent = `# Staging Environment Configuration
|
|
1449
|
+
# Generated by Clodo Framework GenerationEngine
|
|
1450
|
+
|
|
1451
|
+
# Service Configuration
|
|
1452
|
+
SERVICE_NAME=${coreInputs.serviceName}
|
|
1453
|
+
SERVICE_TYPE=${coreInputs.serviceType}
|
|
1454
|
+
ENVIRONMENT=staging
|
|
1455
|
+
|
|
1456
|
+
# URLs
|
|
1457
|
+
PRODUCTION_URL=${confirmedValues.productionUrl}
|
|
1458
|
+
STAGING_URL=${confirmedValues.stagingUrl}
|
|
1459
|
+
DEVELOPMENT_URL=${confirmedValues.developmentUrl}
|
|
1460
|
+
|
|
1461
|
+
# Cloudflare Configuration
|
|
1462
|
+
CLOUDFLARE_ACCOUNT_ID=${coreInputs.cloudflareAccountId}
|
|
1463
|
+
CLOUDFLARE_ZONE_ID=${coreInputs.cloudflareZoneId}
|
|
1464
|
+
|
|
1465
|
+
# Database
|
|
1466
|
+
DATABASE_NAME=${confirmedValues.databaseName}
|
|
1467
|
+
|
|
1468
|
+
# Logging and Monitoring
|
|
1469
|
+
LOG_LEVEL=info
|
|
1470
|
+
METRICS_ENABLED=true
|
|
1471
|
+
ERROR_REPORTING_ENABLED=true
|
|
1472
|
+
`;
|
|
1473
|
+
const filePath = join(servicePath, 'config', 'staging.env');
|
|
1474
|
+
writeFileSync(filePath, envContent, 'utf8');
|
|
1475
|
+
return filePath;
|
|
1476
|
+
}
|
|
1477
|
+
generateDevelopmentEnv(coreInputs, confirmedValues, servicePath) {
|
|
1478
|
+
const envContent = `# Development Environment Configuration
|
|
1479
|
+
# Generated by Clodo Framework GenerationEngine
|
|
1480
|
+
|
|
1481
|
+
# Service Configuration
|
|
1482
|
+
SERVICE_NAME=${coreInputs.serviceName}
|
|
1483
|
+
SERVICE_TYPE=${coreInputs.serviceType}
|
|
1484
|
+
ENVIRONMENT=development
|
|
1485
|
+
|
|
1486
|
+
# URLs
|
|
1487
|
+
PRODUCTION_URL=${confirmedValues.productionUrl}
|
|
1488
|
+
STAGING_URL=${confirmedValues.stagingUrl}
|
|
1489
|
+
DEVELOPMENT_URL=${confirmedValues.developmentUrl}
|
|
1490
|
+
|
|
1491
|
+
# Cloudflare Configuration
|
|
1492
|
+
CLOUDFLARE_ACCOUNT_ID=${coreInputs.cloudflareAccountId}
|
|
1493
|
+
CLOUDFLARE_ZONE_ID=${coreInputs.cloudflareZoneId}
|
|
1494
|
+
|
|
1495
|
+
# Database
|
|
1496
|
+
DATABASE_NAME=${confirmedValues.databaseName}
|
|
1497
|
+
|
|
1498
|
+
# Logging and Monitoring
|
|
1499
|
+
LOG_LEVEL=debug
|
|
1500
|
+
METRICS_ENABLED=false
|
|
1501
|
+
ERROR_REPORTING_ENABLED=false
|
|
1502
|
+
|
|
1503
|
+
# Development settings
|
|
1504
|
+
DEBUG_MODE=true
|
|
1505
|
+
HOT_RELOAD=true
|
|
1506
|
+
`;
|
|
1507
|
+
const filePath = join(servicePath, 'config', 'development.env');
|
|
1508
|
+
writeFileSync(filePath, envContent, 'utf8');
|
|
1509
|
+
return filePath;
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
/**
|
|
1513
|
+
* Generate unit tests
|
|
1514
|
+
*/
|
|
1515
|
+
generateUnitTests(coreInputs, confirmedValues, servicePath) {
|
|
1516
|
+
const testContent = `/**
|
|
1517
|
+
* ${confirmedValues.displayName} - Unit Tests
|
|
1518
|
+
*
|
|
1519
|
+
* Generated by Clodo Framework GenerationEngine
|
|
1520
|
+
*/
|
|
1521
|
+
|
|
1522
|
+
import { describe, test, expect, beforeEach, afterEach } from '@jest/globals';
|
|
1523
|
+
import { createServiceHandlers } from '../../src/handlers/service-handlers.js';
|
|
1524
|
+
import { createServiceMiddleware } from '../../src/middleware/service-middleware.js';
|
|
1525
|
+
import { domains } from '../../src/config/domains.js';
|
|
1526
|
+
|
|
1527
|
+
describe('${confirmedValues.displayName} Service', () => {
|
|
1528
|
+
let mockEnv;
|
|
1529
|
+
let serviceConfig;
|
|
1530
|
+
|
|
1531
|
+
beforeEach(() => {
|
|
1532
|
+
// Mock environment
|
|
1533
|
+
mockEnv = {
|
|
1534
|
+
DB: {
|
|
1535
|
+
prepare: jest.fn(() => ({
|
|
1536
|
+
first: jest.fn().mockResolvedValue({ health_check: 1 }),
|
|
1537
|
+
all: jest.fn().mockResolvedValue({ results: [], meta: {} })
|
|
1538
|
+
}))
|
|
1539
|
+
}
|
|
1540
|
+
};
|
|
1541
|
+
|
|
1542
|
+
serviceConfig = domains['${coreInputs.serviceName}'];
|
|
1543
|
+
});
|
|
1544
|
+
|
|
1545
|
+
describe('Health Check Handler', () => {
|
|
1546
|
+
test('should return healthy status', async () => {
|
|
1547
|
+
const handlers = createServiceHandlers(serviceConfig, mockEnv);
|
|
1548
|
+
const request = new Request('http://localhost${confirmedValues.healthCheckPath}');
|
|
1549
|
+
|
|
1550
|
+
const response = await handlers.handleHealthCheck(request, serviceConfig);
|
|
1551
|
+
const data = await response.json();
|
|
1552
|
+
|
|
1553
|
+
expect(response.status).toBe(200);
|
|
1554
|
+
expect(data.status).toBe('healthy');
|
|
1555
|
+
expect(data.service).toBe('${coreInputs.serviceName}');
|
|
1556
|
+
expect(data.version).toBe('${confirmedValues.version}');
|
|
1557
|
+
});
|
|
1558
|
+
|
|
1559
|
+
test('should handle database errors gracefully', async () => {
|
|
1560
|
+
const failingEnv = {
|
|
1561
|
+
DB: {
|
|
1562
|
+
prepare: jest.fn(() => ({
|
|
1563
|
+
first: jest.fn().mockRejectedValue(new Error('Database connection failed'))
|
|
1564
|
+
}))
|
|
1565
|
+
}
|
|
1566
|
+
};
|
|
1567
|
+
|
|
1568
|
+
const handlers = createServiceHandlers(serviceConfig, failingEnv);
|
|
1569
|
+
const request = new Request('http://localhost${confirmedValues.healthCheckPath}');
|
|
1570
|
+
|
|
1571
|
+
const response = await handlers.handleHealthCheck(request, serviceConfig);
|
|
1572
|
+
const data = await response.json();
|
|
1573
|
+
|
|
1574
|
+
expect(response.status).toBe(200); // Health check still returns 200 but with unhealthy status
|
|
1575
|
+
expect(data.status).toBe('unhealthy');
|
|
1576
|
+
});
|
|
1577
|
+
});
|
|
1578
|
+
|
|
1579
|
+
describe('API Request Handler', () => {
|
|
1580
|
+
test('should handle GET requests', async () => {
|
|
1581
|
+
const handlers = createServiceHandlers(serviceConfig, mockEnv);
|
|
1582
|
+
const request = new Request('http://localhost${confirmedValues.apiBasePath}/test', {
|
|
1583
|
+
method: 'GET'
|
|
1584
|
+
});
|
|
1585
|
+
|
|
1586
|
+
const response = await handlers.handleApiRequest(request, {}, serviceConfig, mockEnv);
|
|
1587
|
+
const data = await response.json();
|
|
1588
|
+
|
|
1589
|
+
expect(response.status).toBe(200);
|
|
1590
|
+
expect(data.message).toContain('GET handler not implemented');
|
|
1591
|
+
});
|
|
1592
|
+
|
|
1593
|
+
test('should handle POST requests', async () => {
|
|
1594
|
+
const handlers = createServiceHandlers(serviceConfig, mockEnv);
|
|
1595
|
+
const request = new Request('http://localhost${confirmedValues.apiBasePath}/test', {
|
|
1596
|
+
method: 'POST'
|
|
1597
|
+
});
|
|
1598
|
+
|
|
1599
|
+
const response = await handlers.handleApiRequest(request, {}, serviceConfig, mockEnv);
|
|
1600
|
+
const data = await response.json();
|
|
1601
|
+
|
|
1602
|
+
expect(response.status).toBe(200);
|
|
1603
|
+
expect(data.message).toContain('POST handler not implemented');
|
|
1604
|
+
});
|
|
1605
|
+
|
|
1606
|
+
test('should return 405 for unsupported methods', async () => {
|
|
1607
|
+
const handlers = createServiceHandlers(serviceConfig, mockEnv);
|
|
1608
|
+
const request = new Request('http://localhost${confirmedValues.apiBasePath}/test', {
|
|
1609
|
+
method: 'PATCH'
|
|
1610
|
+
});
|
|
1611
|
+
|
|
1612
|
+
const response = await handlers.handleApiRequest(request, {}, serviceConfig, mockEnv);
|
|
1613
|
+
const data = await response.json();
|
|
1614
|
+
|
|
1615
|
+
expect(response.status).toBe(405);
|
|
1616
|
+
expect(data.error).toBe('Method Not Allowed');
|
|
1617
|
+
});
|
|
1618
|
+
});
|
|
1619
|
+
|
|
1620
|
+
describe('Middleware', () => {
|
|
1621
|
+
test('should add service headers to requests', async () => {
|
|
1622
|
+
const middleware = createServiceMiddleware(serviceConfig, mockEnv);
|
|
1623
|
+
const request = new Request('http://localhost/test');
|
|
1624
|
+
|
|
1625
|
+
const processedRequest = await middleware.processRequest(request);
|
|
1626
|
+
|
|
1627
|
+
expect(processedRequest.headers.get('X-Service')).toBe('${coreInputs.serviceName}');
|
|
1628
|
+
expect(processedRequest.headers.get('X-Version')).toBe('${confirmedValues.version}');
|
|
1629
|
+
expect(processedRequest.headers.get('X-Environment')).toBe('${coreInputs.environment}');
|
|
1630
|
+
expect(processedRequest.headers.get('X-Request-ID')).toBeDefined();
|
|
1631
|
+
});
|
|
1632
|
+
|
|
1633
|
+
test('should add CORS headers for API requests', async () => {
|
|
1634
|
+
const middleware = createServiceMiddleware(serviceConfig, mockEnv);
|
|
1635
|
+
const request = new Request('http://localhost${confirmedValues.apiBasePath}/test');
|
|
1636
|
+
|
|
1637
|
+
const processedRequest = await middleware.processRequest(request);
|
|
1638
|
+
|
|
1639
|
+
expect(processedRequest.headers.get('Access-Control-Allow-Origin')).toBe('*');
|
|
1640
|
+
expect(processedRequest.headers.get('Access-Control-Allow-Methods')).toBe('GET, POST, PUT, DELETE, OPTIONS');
|
|
1641
|
+
});
|
|
1642
|
+
});
|
|
1643
|
+
|
|
1644
|
+
describe('404 Handler', () => {
|
|
1645
|
+
test('should return 404 for unknown endpoints', async () => {
|
|
1646
|
+
const handlers = createServiceHandlers(serviceConfig, mockEnv);
|
|
1647
|
+
const request = new Request('http://localhost/unknown-endpoint');
|
|
1648
|
+
|
|
1649
|
+
const response = await handlers.handleRequest(request, {});
|
|
1650
|
+
const data = await response.json();
|
|
1651
|
+
|
|
1652
|
+
expect(response.status).toBe(404);
|
|
1653
|
+
expect(data.error).toBe('Not Found');
|
|
1654
|
+
expect(data.availableEndpoints).toContain('${confirmedValues.healthCheckPath}');
|
|
1655
|
+
});
|
|
1656
|
+
});
|
|
1657
|
+
});
|
|
1658
|
+
`;
|
|
1659
|
+
const filePath = join(servicePath, 'test', 'unit', 'service.test.js');
|
|
1660
|
+
writeFileSync(filePath, testContent, 'utf8');
|
|
1661
|
+
return filePath;
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
/**
|
|
1665
|
+
* Generate integration tests
|
|
1666
|
+
*/
|
|
1667
|
+
generateIntegrationTests(coreInputs, confirmedValues, servicePath) {
|
|
1668
|
+
const integrationTestContent = `/**
|
|
1669
|
+
* ${confirmedValues.displayName} - Integration Tests
|
|
1670
|
+
*
|
|
1671
|
+
* Generated by Clodo Framework GenerationEngine
|
|
1672
|
+
*/
|
|
1673
|
+
|
|
1674
|
+
import { describe, test, expect, beforeAll, afterAll } from '@jest/globals';
|
|
1675
|
+
|
|
1676
|
+
describe('${confirmedValues.displayName} Integration Tests', () => {
|
|
1677
|
+
let testServer;
|
|
1678
|
+
let baseUrl;
|
|
1679
|
+
|
|
1680
|
+
beforeAll(async () => {
|
|
1681
|
+
// Start test server (this would need to be implemented)
|
|
1682
|
+
// testServer = await startTestServer();
|
|
1683
|
+
// baseUrl = testServer.url;
|
|
1684
|
+
baseUrl = 'http://localhost:8787'; // Placeholder
|
|
1685
|
+
});
|
|
1686
|
+
|
|
1687
|
+
afterAll(async () => {
|
|
1688
|
+
// Stop test server
|
|
1689
|
+
// await testServer.stop();
|
|
1690
|
+
});
|
|
1691
|
+
|
|
1692
|
+
describe('End-to-End Service Flow', () => {
|
|
1693
|
+
test('complete service lifecycle', async () => {
|
|
1694
|
+
// This would test the complete flow from request to response
|
|
1695
|
+
// Including middleware, handlers, database operations, etc.
|
|
1696
|
+
|
|
1697
|
+
expect(true).toBe(true); // Placeholder test
|
|
1698
|
+
});
|
|
1699
|
+
});
|
|
1700
|
+
|
|
1701
|
+
describe('API Endpoints', () => {
|
|
1702
|
+
test('health check endpoint', async () => {
|
|
1703
|
+
const response = await fetch(\`\${baseUrl}${confirmedValues.healthCheckPath}\`);
|
|
1704
|
+
const data = await response.json();
|
|
1705
|
+
|
|
1706
|
+
expect(response.status).toBe(200);
|
|
1707
|
+
expect(data.status).toBe('healthy');
|
|
1708
|
+
expect(data.service).toBe('${coreInputs.serviceName}');
|
|
1709
|
+
});
|
|
1710
|
+
|
|
1711
|
+
test('API base path routing', async () => {
|
|
1712
|
+
const response = await fetch(\`\${baseUrl}${confirmedValues.apiBasePath}\`);
|
|
1713
|
+
|
|
1714
|
+
// Should not return 404 (service-specific routing would be implemented)
|
|
1715
|
+
expect([200, 404]).toContain(response.status);
|
|
1716
|
+
});
|
|
1717
|
+
});
|
|
1718
|
+
|
|
1719
|
+
describe('Database Integration', () => {
|
|
1720
|
+
${confirmedValues.features.database ? `
|
|
1721
|
+
test('database connectivity', async () => {
|
|
1722
|
+
// Test database operations
|
|
1723
|
+
expect(true).toBe(true); // Placeholder
|
|
1724
|
+
});
|
|
1725
|
+
|
|
1726
|
+
test('CRUD operations', async () => {
|
|
1727
|
+
// Test Create, Read, Update, Delete operations
|
|
1728
|
+
expect(true).toBe(true); // Placeholder
|
|
1729
|
+
});` : `
|
|
1730
|
+
test.skip('database tests skipped - not enabled for this service type', () => {
|
|
1731
|
+
expect(true).toBe(true);
|
|
1732
|
+
});`}
|
|
1733
|
+
});
|
|
1734
|
+
|
|
1735
|
+
describe('Middleware Integration', () => {
|
|
1736
|
+
test('request headers added', async () => {
|
|
1737
|
+
const response = await fetch(\`\${baseUrl}${confirmedValues.healthCheckPath}\`);
|
|
1738
|
+
|
|
1739
|
+
expect(response.headers.get('x-service')).toBe('${coreInputs.serviceName}');
|
|
1740
|
+
expect(response.headers.get('x-version')).toBe('${confirmedValues.version}');
|
|
1741
|
+
});
|
|
1742
|
+
|
|
1743
|
+
${confirmedValues.features.authentication ? `
|
|
1744
|
+
test('authentication middleware', async () => {
|
|
1745
|
+
// Test authentication requirements
|
|
1746
|
+
expect(true).toBe(true); // Placeholder
|
|
1747
|
+
});` : ''}
|
|
1748
|
+
|
|
1749
|
+
${confirmedValues.features.authorization ? `
|
|
1750
|
+
test('authorization middleware', async () => {
|
|
1751
|
+
// Test authorization checks
|
|
1752
|
+
expect(true).toBe(true); // Placeholder
|
|
1753
|
+
});` : ''}
|
|
1754
|
+
});
|
|
1755
|
+
|
|
1756
|
+
describe('Error Handling', () => {
|
|
1757
|
+
test('graceful error responses', async () => {
|
|
1758
|
+
// Test error scenarios
|
|
1759
|
+
expect(true).toBe(true); // Placeholder
|
|
1760
|
+
});
|
|
1761
|
+
|
|
1762
|
+
test('error logging', async () => {
|
|
1763
|
+
// Test error logging functionality
|
|
1764
|
+
expect(true).toBe(true); // Placeholder
|
|
1765
|
+
});
|
|
1766
|
+
});
|
|
1767
|
+
});
|
|
1768
|
+
`;
|
|
1769
|
+
const filePath = join(servicePath, 'test', 'integration', 'service.integration.test.js');
|
|
1770
|
+
writeFileSync(filePath, integrationTestContent, 'utf8');
|
|
1771
|
+
return filePath;
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
/**
|
|
1775
|
+
* Generate Jest configuration
|
|
1776
|
+
*/
|
|
1777
|
+
generateJestConfig(coreInputs, confirmedValues, servicePath) {
|
|
1778
|
+
const jestConfig = `/** @type {import('jest').Config} */
|
|
1779
|
+
export default {
|
|
1780
|
+
testEnvironment: 'node',
|
|
1781
|
+
testMatch: [
|
|
1782
|
+
'<rootDir>/test/**/*.test.js'
|
|
1783
|
+
],
|
|
1784
|
+
collectCoverageFrom: [
|
|
1785
|
+
'src/**/*.js',
|
|
1786
|
+
'!src/worker/index.js' // Worker code runs in different environment
|
|
1787
|
+
],
|
|
1788
|
+
coverageDirectory: 'coverage',
|
|
1789
|
+
coverageReporters: ['text', 'lcov', 'html'],
|
|
1790
|
+
coverageThreshold: {
|
|
1791
|
+
global: {
|
|
1792
|
+
branches: 70,
|
|
1793
|
+
functions: 80,
|
|
1794
|
+
lines: 80,
|
|
1795
|
+
statements: 80
|
|
1796
|
+
}
|
|
1797
|
+
},
|
|
1798
|
+
setupFilesAfterEnv: ['<rootDir>/test/setup.js'],
|
|
1799
|
+
testTimeout: 10000,
|
|
1800
|
+
verbose: true
|
|
1801
|
+
};
|
|
1802
|
+
`;
|
|
1803
|
+
const filePath = join(servicePath, 'jest.config.js');
|
|
1804
|
+
writeFileSync(filePath, jestConfig, 'utf8');
|
|
1805
|
+
return filePath;
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
/**
|
|
1809
|
+
* Generate ESLint configuration
|
|
1810
|
+
*/
|
|
1811
|
+
generateEslintConfig(coreInputs, confirmedValues, servicePath) {
|
|
1812
|
+
const eslintConfig = `export default [
|
|
1813
|
+
{
|
|
1814
|
+
languageOptions: {
|
|
1815
|
+
ecmaVersion: 2022,
|
|
1816
|
+
sourceType: 'module',
|
|
1817
|
+
globals: {
|
|
1818
|
+
console: 'readonly',
|
|
1819
|
+
process: 'readonly',
|
|
1820
|
+
Buffer: 'readonly',
|
|
1821
|
+
crypto: 'readonly',
|
|
1822
|
+
fetch: 'readonly',
|
|
1823
|
+
Request: 'readonly',
|
|
1824
|
+
Response: 'readonly',
|
|
1825
|
+
URL: 'readonly',
|
|
1826
|
+
Headers: 'readonly'
|
|
1827
|
+
}
|
|
1828
|
+
},
|
|
1829
|
+
rules: {
|
|
1830
|
+
'no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
|
|
1831
|
+
'no-console': 'off', // Cloudflare Workers use console
|
|
1832
|
+
'prefer-const': 'error',
|
|
1833
|
+
'no-var': 'error',
|
|
1834
|
+
'object-shorthand': 'error',
|
|
1835
|
+
'prefer-arrow-callback': 'error'
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
];
|
|
1839
|
+
`;
|
|
1840
|
+
const filePath = join(servicePath, '.eslintrc.js');
|
|
1841
|
+
writeFileSync(filePath, eslintConfig, 'utf8');
|
|
1842
|
+
return filePath;
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
/**
|
|
1846
|
+
* Generate README.md
|
|
1847
|
+
*/
|
|
1848
|
+
generateReadme(coreInputs, confirmedValues, servicePath) {
|
|
1849
|
+
const readmeContent = `# ${confirmedValues.displayName}
|
|
1850
|
+
|
|
1851
|
+
${confirmedValues.description}
|
|
1852
|
+
|
|
1853
|
+
## 🚀 Quick Start
|
|
1854
|
+
|
|
1855
|
+
\\\`\\\`\\\`bash
|
|
1856
|
+
# Setup development environment
|
|
1857
|
+
.\\\\scripts\\\\setup.ps1
|
|
1858
|
+
|
|
1859
|
+
# Start development server
|
|
1860
|
+
npm run dev
|
|
1861
|
+
|
|
1862
|
+
# Run tests
|
|
1863
|
+
npm test
|
|
1864
|
+
|
|
1865
|
+
# Deploy to production
|
|
1866
|
+
.\\\\scripts\\\\deploy.ps1 -Environment production
|
|
1867
|
+
\\\`\\\`\\\`
|
|
1868
|
+
|
|
1869
|
+
## 📋 Features
|
|
1870
|
+
|
|
1871
|
+
${Object.entries(confirmedValues.features).filter(([, enabled]) => enabled).map(([feature]) => `- ✅ ${feature.replace(/([A-Z])/g, ' $1').toLowerCase()}`).join('\n')}
|
|
1872
|
+
|
|
1873
|
+
## 🏗️ Architecture
|
|
1874
|
+
|
|
1875
|
+
This service is built with the **Clodo Framework** and follows a three-tier architecture:
|
|
1876
|
+
|
|
1877
|
+
1. **Input Collection**: Collects and validates service requirements
|
|
1878
|
+
2. **Smart Confirmations**: Generates and confirms derived configuration values
|
|
1879
|
+
3. **Automated Generation**: Creates all necessary configuration files and components
|
|
1880
|
+
|
|
1881
|
+
## 📁 Project Structure
|
|
1882
|
+
|
|
1883
|
+
\`\`\`
|
|
1884
|
+
${coreInputs.serviceName}/
|
|
1885
|
+
├── src/
|
|
1886
|
+
│ ├── config/
|
|
1887
|
+
│ │ └── domains.js # Domain configuration
|
|
1888
|
+
│ ├── worker/
|
|
1889
|
+
│ │ └── index.js # Cloudflare Worker entry point
|
|
1890
|
+
│ ├── handlers/
|
|
1891
|
+
│ │ └── service-handlers.js # Request handlers
|
|
1892
|
+
│ ├── middleware/
|
|
1893
|
+
│ │ └── service-middleware.js # Request/response middleware
|
|
1894
|
+
│ ├── schemas/
|
|
1895
|
+
│ │ └── service-schema.js # Data validation schemas
|
|
1896
|
+
│ └── utils/
|
|
1897
|
+
│ └── service-utils.js # Utility functions
|
|
1898
|
+
├── scripts/
|
|
1899
|
+
│ ├── deploy.ps1 # Deployment script
|
|
1900
|
+
│ ├── setup.ps1 # Environment setup
|
|
1901
|
+
│ └── health-check.ps1 # Health monitoring
|
|
1902
|
+
├── test/
|
|
1903
|
+
│ ├── unit/ # Unit tests
|
|
1904
|
+
│ └── integration/ # Integration tests
|
|
1905
|
+
├── config/ # Environment configurations
|
|
1906
|
+
├── docs/ # Documentation
|
|
1907
|
+
└── wrangler.toml # Cloudflare Workers config
|
|
1908
|
+
\`\`\`
|
|
1909
|
+
|
|
1910
|
+
## 🔧 Configuration
|
|
1911
|
+
|
|
1912
|
+
### Environment Variables
|
|
1913
|
+
|
|
1914
|
+
Copy \`.env.example\` to \`.env\` and configure:
|
|
1915
|
+
|
|
1916
|
+
\`\`\`bash
|
|
1917
|
+
# Cloudflare Configuration
|
|
1918
|
+
CLOUDFLARE_ACCOUNT_ID=your_account_id
|
|
1919
|
+
CLOUDFLARE_ZONE_ID=your_zone_id
|
|
1920
|
+
CLOUDFLARE_API_TOKEN=your_api_token
|
|
1921
|
+
|
|
1922
|
+
# Service Configuration
|
|
1923
|
+
SERVICE_NAME=${coreInputs.serviceName}
|
|
1924
|
+
SERVICE_TYPE=${coreInputs.serviceType}
|
|
1925
|
+
DOMAIN_NAME=${coreInputs.domainName}
|
|
1926
|
+
ENVIRONMENT=${coreInputs.environment}
|
|
1927
|
+
\`\`\`
|
|
1928
|
+
|
|
1929
|
+
### Service URLs
|
|
1930
|
+
|
|
1931
|
+
- **Production**: ${confirmedValues.productionUrl}
|
|
1932
|
+
- **Staging**: ${confirmedValues.stagingUrl}
|
|
1933
|
+
- **Development**: ${confirmedValues.developmentUrl}
|
|
1934
|
+
- **Documentation**: ${confirmedValues.documentationUrl}
|
|
1935
|
+
|
|
1936
|
+
## 🧪 Testing
|
|
1937
|
+
|
|
1938
|
+
\`\`\`bash
|
|
1939
|
+
# Run all tests
|
|
1940
|
+
npm test
|
|
1941
|
+
|
|
1942
|
+
# Run with coverage
|
|
1943
|
+
npm run test:coverage
|
|
1944
|
+
|
|
1945
|
+
# Run integration tests only
|
|
1946
|
+
npm test -- --testPathPattern=integration
|
|
1947
|
+
\`\`\`
|
|
1948
|
+
|
|
1949
|
+
## 🚀 Deployment
|
|
1950
|
+
|
|
1951
|
+
### Development
|
|
1952
|
+
|
|
1953
|
+
\`\`\`bash
|
|
1954
|
+
npm run dev
|
|
1955
|
+
\`\`\`
|
|
1956
|
+
|
|
1957
|
+
### Staging
|
|
1958
|
+
|
|
1959
|
+
\`\`\`bash
|
|
1960
|
+
.\\scripts\\deploy.ps1 -Environment staging
|
|
1961
|
+
\`\`\`
|
|
1962
|
+
|
|
1963
|
+
### Production
|
|
1964
|
+
|
|
1965
|
+
\`\`\`bash
|
|
1966
|
+
.\\scripts\\deploy.ps1 -Environment production
|
|
1967
|
+
\`\`\`
|
|
1968
|
+
|
|
1969
|
+
## 🏥 Health Checks
|
|
1970
|
+
|
|
1971
|
+
\`\`\`bash
|
|
1972
|
+
# Check service health
|
|
1973
|
+
.\\scripts\\health-check.ps1 -Environment production
|
|
1974
|
+
\`\`\`
|
|
1975
|
+
|
|
1976
|
+
Health check endpoint: \`${confirmedValues.healthCheckPath}\`
|
|
1977
|
+
|
|
1978
|
+
## 📚 API Documentation
|
|
1979
|
+
|
|
1980
|
+
API Base Path: \`${confirmedValues.apiBasePath}\`
|
|
1981
|
+
|
|
1982
|
+
See [API Documentation](./docs/API.md) for detailed endpoint information.
|
|
1983
|
+
|
|
1984
|
+
## 🤝 Contributing
|
|
1985
|
+
|
|
1986
|
+
1. Fork the repository
|
|
1987
|
+
2. Create a feature branch
|
|
1988
|
+
3. Make your changes
|
|
1989
|
+
4. Add tests
|
|
1990
|
+
5. Submit a pull request
|
|
1991
|
+
|
|
1992
|
+
## 📄 License
|
|
1993
|
+
|
|
1994
|
+
${confirmedValues.author} - Generated by Clodo Framework v3.0.0
|
|
1995
|
+
|
|
1996
|
+
## 🔗 Links
|
|
1997
|
+
|
|
1998
|
+
- **Repository**: ${confirmedValues.gitRepositoryUrl}
|
|
1999
|
+
- **Documentation**: ${confirmedValues.documentationUrl}
|
|
2000
|
+
- **Health Check**: ${confirmedValues.productionUrl}${confirmedValues.healthCheckPath}
|
|
2001
|
+
`;
|
|
2002
|
+
const filePath = join(servicePath, 'README.md');
|
|
2003
|
+
writeFileSync(filePath, readmeContent, 'utf8');
|
|
2004
|
+
return filePath;
|
|
2005
|
+
}
|
|
2006
|
+
|
|
2007
|
+
/**
|
|
2008
|
+
* Generate API documentation
|
|
2009
|
+
*/
|
|
2010
|
+
generateApiDocs(coreInputs, confirmedValues, servicePath) {
|
|
2011
|
+
const apiDocsContent = `# ${confirmedValues.displayName} API Documentation
|
|
2012
|
+
|
|
2013
|
+
## Overview
|
|
2014
|
+
|
|
2015
|
+
${confirmedValues.description}
|
|
2016
|
+
|
|
2017
|
+
**Base URL**: ${confirmedValues.productionUrl}
|
|
2018
|
+
**API Base Path**: ${confirmedValues.apiBasePath}
|
|
2019
|
+
**Version**: ${confirmedValues.version}
|
|
2020
|
+
|
|
2021
|
+
## Authentication
|
|
2022
|
+
|
|
2023
|
+
${confirmedValues.features.authentication ? 'This service requires authentication. Include your API key in the request headers:\n\n' + '```\nAuthorization: Bearer YOUR_API_KEY\n```' : 'This service does not require authentication.'}
|
|
2024
|
+
|
|
2025
|
+
## Endpoints
|
|
2026
|
+
|
|
2027
|
+
### Health Check
|
|
2028
|
+
|
|
2029
|
+
**GET** ${confirmedValues.healthCheckPath}
|
|
2030
|
+
|
|
2031
|
+
Check the health status of the service.
|
|
2032
|
+
|
|
2033
|
+
**Response:**
|
|
2034
|
+
\`\`\`json
|
|
2035
|
+
{
|
|
2036
|
+
"status": "healthy",
|
|
2037
|
+
"timestamp": "2024-01-01T00:00:00.000Z",
|
|
2038
|
+
"service": "${coreInputs.serviceName}",
|
|
2039
|
+
"version": "${confirmedValues.version}",
|
|
2040
|
+
"environment": "${coreInputs.environment}",
|
|
2041
|
+
"checks": [
|
|
2042
|
+
{
|
|
2043
|
+
"name": "database",
|
|
2044
|
+
"status": "healthy"
|
|
2045
|
+
},
|
|
2046
|
+
{
|
|
2047
|
+
"name": "configuration",
|
|
2048
|
+
"status": "healthy"
|
|
2049
|
+
}
|
|
2050
|
+
]
|
|
2051
|
+
}
|
|
2052
|
+
\`\`\`
|
|
2053
|
+
|
|
2054
|
+
### API Endpoints
|
|
2055
|
+
|
|
2056
|
+
**Base Path**: ${confirmedValues.apiBasePath}
|
|
2057
|
+
|
|
2058
|
+
${this.generateApiEndpointsForType(coreInputs.serviceType, coreInputs, confirmedValues)}
|
|
2059
|
+
|
|
2060
|
+
## Error Responses
|
|
2061
|
+
|
|
2062
|
+
All error responses follow this format:
|
|
2063
|
+
|
|
2064
|
+
\`\`\`json
|
|
2065
|
+
{
|
|
2066
|
+
"success": false,
|
|
2067
|
+
"error": "Error message",
|
|
2068
|
+
"timestamp": "2024-01-01T00:00:00.000Z"
|
|
2069
|
+
}
|
|
2070
|
+
\`\`\`
|
|
2071
|
+
|
|
2072
|
+
### Common HTTP Status Codes
|
|
2073
|
+
|
|
2074
|
+
- **200**: Success
|
|
2075
|
+
- **400**: Bad Request
|
|
2076
|
+
- **401**: Unauthorized
|
|
2077
|
+
- **403**: Forbidden
|
|
2078
|
+
- **404**: Not Found
|
|
2079
|
+
- **500**: Internal Server Error
|
|
2080
|
+
|
|
2081
|
+
## Rate Limiting
|
|
2082
|
+
|
|
2083
|
+
${confirmedValues.features.rateLimiting ? 'This service implements rate limiting. Please respect the following limits:\n\n' + '- 1000 requests per hour for authenticated users\n' + '- 100 requests per hour for anonymous users' : 'This service does not implement rate limiting.'}
|
|
2084
|
+
|
|
2085
|
+
## Data Formats
|
|
2086
|
+
|
|
2087
|
+
All requests and responses use JSON format.
|
|
2088
|
+
|
|
2089
|
+
### Request Headers
|
|
2090
|
+
|
|
2091
|
+
\`\`\`
|
|
2092
|
+
Content-Type: application/json
|
|
2093
|
+
Authorization: Bearer YOUR_API_KEY (if required)
|
|
2094
|
+
\`\`\`
|
|
2095
|
+
|
|
2096
|
+
### Response Headers
|
|
2097
|
+
|
|
2098
|
+
\`\`\`
|
|
2099
|
+
Content-Type: application/json
|
|
2100
|
+
X-Service: ${coreInputs.serviceName}
|
|
2101
|
+
X-Version: ${confirmedValues.version}
|
|
2102
|
+
X-Response-Time: 150
|
|
2103
|
+
\`\`\`
|
|
2104
|
+
`;
|
|
2105
|
+
const filePath = join(servicePath, 'docs', 'API.md');
|
|
2106
|
+
writeFileSync(filePath, apiDocsContent, 'utf8');
|
|
2107
|
+
return filePath;
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
/**
|
|
2111
|
+
* Generate API endpoints based on service type
|
|
2112
|
+
*/
|
|
2113
|
+
generateApiEndpointsForType(serviceType, coreInputs, confirmedValues) {
|
|
2114
|
+
const endpoints = {
|
|
2115
|
+
'data-service': `
|
|
2116
|
+
#### List Items
|
|
2117
|
+
**GET** /items
|
|
2118
|
+
|
|
2119
|
+
Retrieve a paginated list of items.
|
|
2120
|
+
|
|
2121
|
+
**Query Parameters:**
|
|
2122
|
+
- \`limit\` (optional): Number of items per page (default: 20, max: 100)
|
|
2123
|
+
- \`offset\` (optional): Number of items to skip (default: 0)
|
|
2124
|
+
- \`search\` (optional): Search query string
|
|
2125
|
+
- \`filters\` (optional): JSON object with filter criteria
|
|
2126
|
+
|
|
2127
|
+
**Response:**
|
|
2128
|
+
\`\`\`json
|
|
2129
|
+
{
|
|
2130
|
+
"success": true,
|
|
2131
|
+
"data": [...],
|
|
2132
|
+
"pagination": {
|
|
2133
|
+
"limit": 20,
|
|
2134
|
+
"offset": 0,
|
|
2135
|
+
"total": 150,
|
|
2136
|
+
"hasMore": true
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
\`\`\`
|
|
2140
|
+
|
|
2141
|
+
#### Create Item
|
|
2142
|
+
**POST** /items
|
|
2143
|
+
|
|
2144
|
+
Create a new item.
|
|
2145
|
+
|
|
2146
|
+
**Request Body:**
|
|
2147
|
+
\`\`\`json
|
|
2148
|
+
{
|
|
2149
|
+
"name": "Item Name",
|
|
2150
|
+
"description": "Item description",
|
|
2151
|
+
"data": {}
|
|
2152
|
+
}
|
|
2153
|
+
\`\`\`
|
|
2154
|
+
|
|
2155
|
+
#### Get Item
|
|
2156
|
+
**GET** /items/{id}
|
|
2157
|
+
|
|
2158
|
+
Retrieve a specific item by ID.
|
|
2159
|
+
|
|
2160
|
+
#### Update Item
|
|
2161
|
+
**PUT** /items/{id}
|
|
2162
|
+
|
|
2163
|
+
Update an existing item.
|
|
2164
|
+
|
|
2165
|
+
#### Delete Item
|
|
2166
|
+
**DELETE** /items/{id}
|
|
2167
|
+
|
|
2168
|
+
Delete an item.
|
|
2169
|
+
`,
|
|
2170
|
+
'auth-service': `
|
|
2171
|
+
#### Register User
|
|
2172
|
+
**POST** /auth/register
|
|
2173
|
+
|
|
2174
|
+
Register a new user account.
|
|
2175
|
+
|
|
2176
|
+
**Request Body:**
|
|
2177
|
+
\`\`\`json
|
|
2178
|
+
{
|
|
2179
|
+
"email": "user@example.com",
|
|
2180
|
+
"username": "username",
|
|
2181
|
+
"password": "password"
|
|
2182
|
+
}
|
|
2183
|
+
\`\`\`
|
|
2184
|
+
|
|
2185
|
+
#### Login
|
|
2186
|
+
**POST** /auth/login
|
|
2187
|
+
|
|
2188
|
+
Authenticate a user and receive access tokens.
|
|
2189
|
+
|
|
2190
|
+
**Request Body:**
|
|
2191
|
+
\`\`\`json
|
|
2192
|
+
{
|
|
2193
|
+
"email": "user@example.com",
|
|
2194
|
+
"password": "password"
|
|
2195
|
+
}
|
|
2196
|
+
\`\`\`
|
|
2197
|
+
|
|
2198
|
+
#### Get Profile
|
|
2199
|
+
**GET** /auth/profile
|
|
2200
|
+
|
|
2201
|
+
Get the current user's profile information.
|
|
2202
|
+
|
|
2203
|
+
#### Update Profile
|
|
2204
|
+
**PUT** /auth/profile
|
|
2205
|
+
|
|
2206
|
+
Update the current user's profile.
|
|
2207
|
+
|
|
2208
|
+
#### Logout
|
|
2209
|
+
**POST** /auth/logout
|
|
2210
|
+
|
|
2211
|
+
Invalidate the current user's session.
|
|
2212
|
+
`,
|
|
2213
|
+
'content-service': `
|
|
2214
|
+
#### List Content
|
|
2215
|
+
**GET** /content
|
|
2216
|
+
|
|
2217
|
+
Retrieve a list of content items.
|
|
2218
|
+
|
|
2219
|
+
#### Create Content
|
|
2220
|
+
**POST** /content
|
|
2221
|
+
|
|
2222
|
+
Create new content.
|
|
2223
|
+
|
|
2224
|
+
**Request Body:**
|
|
2225
|
+
\`\`\`json
|
|
2226
|
+
{
|
|
2227
|
+
"title": "Content Title",
|
|
2228
|
+
"content": "Content body",
|
|
2229
|
+
"contentType": "article",
|
|
2230
|
+
"tags": ["tag1", "tag2"]
|
|
2231
|
+
}
|
|
2232
|
+
\`\`\`
|
|
2233
|
+
|
|
2234
|
+
#### Get Content
|
|
2235
|
+
**GET** /content/{id}
|
|
2236
|
+
|
|
2237
|
+
Retrieve specific content by ID.
|
|
2238
|
+
|
|
2239
|
+
#### Update Content
|
|
2240
|
+
**PUT** /content/{id}
|
|
2241
|
+
|
|
2242
|
+
Update existing content.
|
|
2243
|
+
|
|
2244
|
+
#### Delete Content
|
|
2245
|
+
**DELETE** /content/{id}
|
|
2246
|
+
|
|
2247
|
+
Delete content.
|
|
2248
|
+
|
|
2249
|
+
#### Upload Media
|
|
2250
|
+
**POST** /media/upload
|
|
2251
|
+
|
|
2252
|
+
Upload media files.
|
|
2253
|
+
`,
|
|
2254
|
+
'api-gateway': `
|
|
2255
|
+
#### Route Request
|
|
2256
|
+
**ANY** /*
|
|
2257
|
+
|
|
2258
|
+
All requests are routed through the API gateway.
|
|
2259
|
+
|
|
2260
|
+
**Headers:**
|
|
2261
|
+
- \`X-Target-Service\`: Target service name
|
|
2262
|
+
- \`X-Target-Path\`: Path on target service
|
|
2263
|
+
|
|
2264
|
+
#### Get Routes
|
|
2265
|
+
**GET** /routes
|
|
2266
|
+
|
|
2267
|
+
List all configured routes.
|
|
2268
|
+
|
|
2269
|
+
#### Health Status
|
|
2270
|
+
**GET** /status
|
|
2271
|
+
|
|
2272
|
+
Get gateway health and route status.
|
|
2273
|
+
`,
|
|
2274
|
+
'generic': `
|
|
2275
|
+
#### Service Info
|
|
2276
|
+
**GET** /info
|
|
2277
|
+
|
|
2278
|
+
Get service information and capabilities.
|
|
2279
|
+
|
|
2280
|
+
**Response:**
|
|
2281
|
+
\`\`\`json
|
|
2282
|
+
{
|
|
2283
|
+
"success": true,
|
|
2284
|
+
"data": {
|
|
2285
|
+
"name": "${coreInputs.serviceName}",
|
|
2286
|
+
"type": "${coreInputs.serviceType}",
|
|
2287
|
+
"version": "${confirmedValues.version}",
|
|
2288
|
+
"features": ${JSON.stringify(Object.keys(confirmedValues.features).filter(key => confirmedValues.features[key]), null, 4)}
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
\`\`\`
|
|
2292
|
+
`
|
|
2293
|
+
};
|
|
2294
|
+
return endpoints[serviceType] || endpoints.generic;
|
|
2295
|
+
}
|
|
2296
|
+
|
|
2297
|
+
/**
|
|
2298
|
+
* Generate deployment documentation
|
|
2299
|
+
*/
|
|
2300
|
+
generateDeploymentDocs(coreInputs, confirmedValues, servicePath) {
|
|
2301
|
+
const deploymentDocsContent = `# ${confirmedValues.displayName} - Deployment Guide
|
|
2302
|
+
|
|
2303
|
+
## Overview
|
|
2304
|
+
|
|
2305
|
+
This guide covers deploying ${confirmedValues.displayName} to different environments using the Clodo Framework.
|
|
2306
|
+
|
|
2307
|
+
## Environments
|
|
2308
|
+
|
|
2309
|
+
### Development
|
|
2310
|
+
- **URL**: ${confirmedValues.developmentUrl}
|
|
2311
|
+
- **Environment**: development
|
|
2312
|
+
- **Configuration**: \`config/development.env\`
|
|
2313
|
+
|
|
2314
|
+
### Staging
|
|
2315
|
+
- **URL**: ${confirmedValues.stagingUrl}
|
|
2316
|
+
- **Environment**: staging
|
|
2317
|
+
- **Configuration**: \`config/staging.env\`
|
|
2318
|
+
|
|
2319
|
+
### Production
|
|
2320
|
+
- **URL**: ${confirmedValues.productionUrl}
|
|
2321
|
+
- **Environment**: production
|
|
2322
|
+
- **Configuration**: \`config/production.env\`
|
|
2323
|
+
|
|
2324
|
+
## Prerequisites
|
|
2325
|
+
|
|
2326
|
+
- Node.js 18+
|
|
2327
|
+
- Cloudflare account with Workers enabled
|
|
2328
|
+
- Wrangler CLI installed
|
|
2329
|
+
- PowerShell (for deployment scripts)
|
|
2330
|
+
|
|
2331
|
+
## Initial Setup
|
|
2332
|
+
|
|
2333
|
+
1. **Clone and setup**:
|
|
2334
|
+
\`\`\`bash
|
|
2335
|
+
git clone ${confirmedValues.gitRepositoryUrl}
|
|
2336
|
+
cd ${coreInputs.serviceName}
|
|
2337
|
+
.\\scripts\\setup.ps1
|
|
2338
|
+
\`\`\`
|
|
2339
|
+
|
|
2340
|
+
2. **Configure environment**:
|
|
2341
|
+
Edit \`.env\` with your Cloudflare credentials:
|
|
2342
|
+
\`\`\`bash
|
|
2343
|
+
CLOUDFLARE_ACCOUNT_ID=your_account_id
|
|
2344
|
+
CLOUDFLARE_ZONE_ID=your_zone_id
|
|
2345
|
+
CLOUDFLARE_API_TOKEN=your_api_token
|
|
2346
|
+
\`\`\`
|
|
2347
|
+
|
|
2348
|
+
3. **Setup database** (if enabled):
|
|
2349
|
+
${confirmedValues.features.database ? `
|
|
2350
|
+
Create a Cloudflare D1 database and update \`wrangler.toml\`:
|
|
2351
|
+
\`\`\`toml
|
|
2352
|
+
[[d1_databases]]
|
|
2353
|
+
binding = "DB"
|
|
2354
|
+
database_name = "${confirmedValues.databaseName}"
|
|
2355
|
+
database_id = "your_database_id"
|
|
2356
|
+
\`\`\`
|
|
2357
|
+
` : 'Database not required for this service type.'}
|
|
2358
|
+
|
|
2359
|
+
## Development Deployment
|
|
2360
|
+
|
|
2361
|
+
\`\`\`bash
|
|
2362
|
+
# Start local development server
|
|
2363
|
+
npm run dev
|
|
2364
|
+
|
|
2365
|
+
# Server will be available at http://localhost:8787
|
|
2366
|
+
\`\`\`
|
|
2367
|
+
|
|
2368
|
+
## Staging Deployment
|
|
2369
|
+
|
|
2370
|
+
\`\`\`bash
|
|
2371
|
+
# Deploy to staging
|
|
2372
|
+
.\\scripts\\deploy.ps1 -Environment staging
|
|
2373
|
+
|
|
2374
|
+
# Run health checks
|
|
2375
|
+
.\\scripts\\health-check.ps1 -Environment staging
|
|
2376
|
+
\`\`\`
|
|
2377
|
+
|
|
2378
|
+
## Production Deployment
|
|
2379
|
+
|
|
2380
|
+
\`\`\`bash
|
|
2381
|
+
# Deploy to production
|
|
2382
|
+
.\\scripts\\deploy.ps1 -Environment production
|
|
2383
|
+
|
|
2384
|
+
# Verify deployment
|
|
2385
|
+
.\\scripts\\health-check.ps1 -Environment production
|
|
2386
|
+
\`\`\`
|
|
2387
|
+
|
|
2388
|
+
## Automated Deployment
|
|
2389
|
+
|
|
2390
|
+
### GitHub Actions
|
|
2391
|
+
|
|
2392
|
+
The service includes GitHub Actions workflows for automated deployment:
|
|
2393
|
+
|
|
2394
|
+
- **CI**: Runs on every push to main branch
|
|
2395
|
+
- **Deploy**: Deploys to staging on successful CI
|
|
2396
|
+
- **Release**: Deploys to production on tag creation
|
|
2397
|
+
|
|
2398
|
+
### Manual CI/CD
|
|
2399
|
+
|
|
2400
|
+
\`\`\`bash
|
|
2401
|
+
# Run full CI pipeline locally
|
|
2402
|
+
npm run lint
|
|
2403
|
+
npm test
|
|
2404
|
+
npm run build
|
|
2405
|
+
|
|
2406
|
+
# Deploy if all checks pass
|
|
2407
|
+
.\\scripts\\deploy.ps1 -Environment production
|
|
2408
|
+
\`\`\`
|
|
2409
|
+
|
|
2410
|
+
## Monitoring and Health Checks
|
|
2411
|
+
|
|
2412
|
+
### Health Check Endpoint
|
|
2413
|
+
|
|
2414
|
+
\`\`\`bash
|
|
2415
|
+
curl ${confirmedValues.productionUrl}${confirmedValues.healthCheckPath}
|
|
2416
|
+
\`\`\`
|
|
2417
|
+
|
|
2418
|
+
### Automated Health Monitoring
|
|
2419
|
+
|
|
2420
|
+
The deployment scripts include automated health checks. For production monitoring, consider:
|
|
2421
|
+
|
|
2422
|
+
- Cloudflare Analytics
|
|
2423
|
+
- External monitoring services
|
|
2424
|
+
- Log aggregation tools
|
|
2425
|
+
|
|
2426
|
+
## Rollback Strategy
|
|
2427
|
+
|
|
2428
|
+
### Quick Rollback
|
|
2429
|
+
|
|
2430
|
+
\`\`\`bash
|
|
2431
|
+
# Deploy previous version
|
|
2432
|
+
wrangler deploy --env production
|
|
2433
|
+
|
|
2434
|
+
# Or redeploy from git
|
|
2435
|
+
git checkout previous-version
|
|
2436
|
+
npm run deploy
|
|
2437
|
+
\`\`\`
|
|
2438
|
+
|
|
2439
|
+
### Database Rollback
|
|
2440
|
+
|
|
2441
|
+
${confirmedValues.features.database ? `
|
|
2442
|
+
If database schema changes need rollback:
|
|
2443
|
+
|
|
2444
|
+
1. Restore from backup
|
|
2445
|
+
2. Run migration rollback scripts
|
|
2446
|
+
3. Update wrangler.toml if needed
|
|
2447
|
+
` : 'No database rollback required for this service type.'}
|
|
2448
|
+
|
|
2449
|
+
## Troubleshooting
|
|
2450
|
+
|
|
2451
|
+
### Common Issues
|
|
2452
|
+
|
|
2453
|
+
1. **Deployment fails with authentication error**
|
|
2454
|
+
- Check Cloudflare API token permissions
|
|
2455
|
+
- Verify account ID and zone ID
|
|
2456
|
+
|
|
2457
|
+
2. **Health check fails**
|
|
2458
|
+
- Check database connectivity
|
|
2459
|
+
- Verify environment variables
|
|
2460
|
+
- Review worker logs
|
|
2461
|
+
|
|
2462
|
+
3. **API returns 500 errors**
|
|
2463
|
+
- Check worker logs in Cloudflare dashboard
|
|
2464
|
+
- Verify service configuration
|
|
2465
|
+
- Test locally first
|
|
2466
|
+
|
|
2467
|
+
### Logs and Debugging
|
|
2468
|
+
|
|
2469
|
+
\`\`\`bash
|
|
2470
|
+
# View worker logs
|
|
2471
|
+
wrangler tail
|
|
2472
|
+
|
|
2473
|
+
# Check deployment status
|
|
2474
|
+
wrangler deployments list
|
|
2475
|
+
|
|
2476
|
+
# View environment info
|
|
2477
|
+
wrangler whoami
|
|
2478
|
+
\`\`\`
|
|
2479
|
+
|
|
2480
|
+
## Security Considerations
|
|
2481
|
+
|
|
2482
|
+
- Store secrets in Cloudflare Workers secrets, not environment variables
|
|
2483
|
+
- Use HTTPS for all production endpoints
|
|
2484
|
+
- Implement proper authentication and authorization
|
|
2485
|
+
- Regularly rotate API tokens
|
|
2486
|
+
- Monitor for unusual activity
|
|
2487
|
+
|
|
2488
|
+
## Performance Optimization
|
|
2489
|
+
|
|
2490
|
+
- Enable caching where appropriate
|
|
2491
|
+
- Use appropriate database indexes
|
|
2492
|
+
- Monitor response times
|
|
2493
|
+
- Optimize bundle size
|
|
2494
|
+
- Consider edge deployment locations
|
|
2495
|
+
`;
|
|
2496
|
+
const filePath = join(servicePath, 'docs', 'DEPLOYMENT.md');
|
|
2497
|
+
writeFileSync(filePath, deploymentDocsContent, 'utf8');
|
|
2498
|
+
return filePath;
|
|
2499
|
+
}
|
|
2500
|
+
|
|
2501
|
+
/**
|
|
2502
|
+
* Generate configuration documentation
|
|
2503
|
+
*/
|
|
2504
|
+
generateConfigurationDocs(coreInputs, confirmedValues, servicePath) {
|
|
2505
|
+
const configDocsContent = `# ${confirmedValues.displayName} - Configuration Guide
|
|
2506
|
+
|
|
2507
|
+
## Overview
|
|
2508
|
+
|
|
2509
|
+
${confirmedValues.displayName} is configured using multiple layers of configuration files and environment variables.
|
|
2510
|
+
|
|
2511
|
+
## Configuration Hierarchy
|
|
2512
|
+
|
|
2513
|
+
1. **Environment Variables** (.env) - Runtime secrets and environment-specific values
|
|
2514
|
+
2. **Service Configuration** (src/config/domains.js) - Service-specific settings
|
|
2515
|
+
3. **Worker Configuration** (wrangler.toml) - Cloudflare Workers deployment settings
|
|
2516
|
+
4. **Package Configuration** (package.json) - Node.js package settings
|
|
2517
|
+
|
|
2518
|
+
## Environment Variables
|
|
2519
|
+
|
|
2520
|
+
### Required Variables
|
|
2521
|
+
|
|
2522
|
+
\`\`\`bash
|
|
2523
|
+
# Cloudflare Configuration
|
|
2524
|
+
CLOUDFLARE_ACCOUNT_ID=${coreInputs.cloudflareAccountId}
|
|
2525
|
+
CLOUDFLARE_ZONE_ID=${coreInputs.cloudflareZoneId}
|
|
2526
|
+
CLOUDFLARE_API_TOKEN=your_api_token
|
|
2527
|
+
|
|
2528
|
+
# Service Configuration
|
|
2529
|
+
SERVICE_NAME=${coreInputs.serviceName}
|
|
2530
|
+
SERVICE_TYPE=${coreInputs.serviceType}
|
|
2531
|
+
DOMAIN_NAME=${coreInputs.domainName}
|
|
2532
|
+
ENVIRONMENT=${coreInputs.environment}
|
|
2533
|
+
\`\`\`
|
|
2534
|
+
|
|
2535
|
+
### Optional Variables
|
|
2536
|
+
|
|
2537
|
+
\`\`\`bash
|
|
2538
|
+
# URLs (override defaults)
|
|
2539
|
+
PRODUCTION_URL=${confirmedValues.productionUrl}
|
|
2540
|
+
STAGING_URL=${confirmedValues.stagingUrl}
|
|
2541
|
+
DEVELOPMENT_URL=${confirmedValues.developmentUrl}
|
|
2542
|
+
DOCUMENTATION_URL=${confirmedValues.documentationUrl}
|
|
2543
|
+
|
|
2544
|
+
# API Configuration
|
|
2545
|
+
API_BASE_PATH=${confirmedValues.apiBasePath}
|
|
2546
|
+
HEALTH_CHECK_PATH=${confirmedValues.healthCheckPath}
|
|
2547
|
+
|
|
2548
|
+
# Database
|
|
2549
|
+
DATABASE_NAME=${confirmedValues.databaseName}
|
|
2550
|
+
|
|
2551
|
+
# Logging and Monitoring
|
|
2552
|
+
LOG_LEVEL=info
|
|
2553
|
+
METRICS_ENABLED=true
|
|
2554
|
+
ERROR_REPORTING_ENABLED=true
|
|
2555
|
+
|
|
2556
|
+
# Custom Variables
|
|
2557
|
+
CUSTOM_VAR=value
|
|
2558
|
+
\`\`\`
|
|
2559
|
+
|
|
2560
|
+
## Service Configuration (domains.js)
|
|
2561
|
+
|
|
2562
|
+
Located at \`src/config/domains.js\`, this file contains service-specific configuration:
|
|
2563
|
+
|
|
2564
|
+
\`\`\`javascript
|
|
2565
|
+
export const domains = {
|
|
2566
|
+
'${coreInputs.serviceName}': {
|
|
2567
|
+
name: '${coreInputs.serviceName}',
|
|
2568
|
+
displayName: '${confirmedValues.displayName}',
|
|
2569
|
+
description: '${confirmedValues.description}',
|
|
2570
|
+
accountId: process.env.CLOUDFLARE_ACCOUNT_ID,
|
|
2571
|
+
zoneId: process.env.CLOUDFLARE_ZONE_ID,
|
|
2572
|
+
domains: {
|
|
2573
|
+
production: '${confirmedValues.productionUrl}',
|
|
2574
|
+
staging: '${confirmedValues.stagingUrl}',
|
|
2575
|
+
development: '${confirmedValues.developmentUrl}'
|
|
2576
|
+
},
|
|
2577
|
+
services: [
|
|
2578
|
+
'${coreInputs.serviceName}'
|
|
2579
|
+
],
|
|
2580
|
+
databases: [
|
|
2581
|
+
{
|
|
2582
|
+
name: '${confirmedValues.databaseName}',
|
|
2583
|
+
type: 'd1',
|
|
2584
|
+
binding: 'DB'
|
|
2585
|
+
}
|
|
2586
|
+
],
|
|
2587
|
+
features: ${JSON.stringify(confirmedValues.features, null, 4)},
|
|
2588
|
+
metadata: {
|
|
2589
|
+
version: '${confirmedValues.version}',
|
|
2590
|
+
author: '${confirmedValues.author}',
|
|
2591
|
+
generatedAt: '${new Date().toISOString()}',
|
|
2592
|
+
frameworkVersion: '3.0.0',
|
|
2593
|
+
serviceType: '${coreInputs.serviceType}',
|
|
2594
|
+
environment: '${coreInputs.environment}'
|
|
2595
|
+
}
|
|
2596
|
+
}
|
|
2597
|
+
};
|
|
2598
|
+
\`\`\`
|
|
2599
|
+
|
|
2600
|
+
## Worker Configuration (wrangler.toml)
|
|
2601
|
+
|
|
2602
|
+
Cloudflare Workers configuration with environment-specific settings:
|
|
2603
|
+
|
|
2604
|
+
\`\`\`toml
|
|
2605
|
+
name = "${confirmedValues.workerName}"
|
|
2606
|
+
main = "src/worker/index.js"
|
|
2607
|
+
compatibility_date = "${new Date().toISOString().split('T')[0]}"
|
|
2608
|
+
compatibility_flags = ["nodejs_compat"]
|
|
2609
|
+
|
|
2610
|
+
# Environment configurations
|
|
2611
|
+
[env.development]
|
|
2612
|
+
name = "${confirmedValues.workerName}-dev"
|
|
2613
|
+
|
|
2614
|
+
[env.staging]
|
|
2615
|
+
name = "${confirmedValues.workerName}-staging"
|
|
2616
|
+
|
|
2617
|
+
[env.production]
|
|
2618
|
+
name = "${confirmedValues.workerName}"
|
|
2619
|
+
|
|
2620
|
+
# Database bindings
|
|
2621
|
+
[[d1_databases]]
|
|
2622
|
+
binding = "DB"
|
|
2623
|
+
database_name = "${confirmedValues.databaseName}"
|
|
2624
|
+
|
|
2625
|
+
# Environment variables
|
|
2626
|
+
[vars]
|
|
2627
|
+
SERVICE_NAME = "${coreInputs.serviceName}"
|
|
2628
|
+
SERVICE_TYPE = "${coreInputs.serviceType}"
|
|
2629
|
+
DOMAIN_NAME = "${coreInputs.domainName}"
|
|
2630
|
+
ENVIRONMENT = "${coreInputs.environment}"
|
|
2631
|
+
API_BASE_PATH = "${confirmedValues.apiBasePath}"
|
|
2632
|
+
HEALTH_CHECK_PATH = "${confirmedValues.healthCheckPath}"
|
|
2633
|
+
|
|
2634
|
+
# Domain-specific variables
|
|
2635
|
+
PRODUCTION_URL = "${confirmedValues.productionUrl}"
|
|
2636
|
+
STAGING_URL = "${confirmedValues.stagingUrl}"
|
|
2637
|
+
DEVELOPMENT_URL = "${confirmedValues.developmentUrl}"
|
|
2638
|
+
|
|
2639
|
+
# Feature flags
|
|
2640
|
+
${Object.entries(confirmedValues.features).filter(([, enabled]) => enabled).map(([feature, enabled]) => `FEATURE_${feature.toUpperCase()} = ${enabled}`).join('\n')}
|
|
2641
|
+
|
|
2642
|
+
# Custom environment variables (configure as needed)
|
|
2643
|
+
# CUSTOM_VAR = "value"
|
|
2644
|
+
\`\`\`
|
|
2645
|
+
|
|
2646
|
+
## Feature Flags
|
|
2647
|
+
|
|
2648
|
+
The service supports the following feature flags:
|
|
2649
|
+
|
|
2650
|
+
${Object.entries(confirmedValues.features).map(([feature, enabled]) => `- **${feature}**: ${enabled ? '✅ Enabled' : '❌ Disabled'}`).join('\n')}
|
|
2651
|
+
|
|
2652
|
+
### Feature Descriptions
|
|
2653
|
+
|
|
2654
|
+
- **logging**: Request/response logging
|
|
2655
|
+
- **monitoring**: Performance monitoring and metrics
|
|
2656
|
+
- **errorReporting**: Error tracking and reporting
|
|
2657
|
+
- **metrics**: Application metrics collection
|
|
2658
|
+
- **healthChecks**: Health check endpoints
|
|
2659
|
+
${confirmedValues.features.database ? '- **database**: Database operations and connectivity\n' : ''}
|
|
2660
|
+
${confirmedValues.features.authentication ? '- **authentication**: User authentication\n' : ''}
|
|
2661
|
+
${confirmedValues.features.authorization ? '- **authorization**: Access control and permissions\n' : ''}
|
|
2662
|
+
${confirmedValues.features.search ? '- **search**: Search functionality\n' : ''}
|
|
2663
|
+
${confirmedValues.features.filtering ? '- **filtering**: Data filtering capabilities\n' : ''}
|
|
2664
|
+
${confirmedValues.features.pagination ? '- **pagination**: Paginated responses\n' : ''}
|
|
2665
|
+
${confirmedValues.features.caching ? '- **caching**: Response caching\n' : ''}
|
|
2666
|
+
${confirmedValues.features.backup ? '- **backup**: Data backup functionality\n' : ''}
|
|
2667
|
+
|
|
2668
|
+
## Environment-Specific Configuration
|
|
2669
|
+
|
|
2670
|
+
### Development
|
|
2671
|
+
- Full debugging enabled
|
|
2672
|
+
- Local database connections
|
|
2673
|
+
- Hot reload enabled
|
|
2674
|
+
- Less strict validation
|
|
2675
|
+
|
|
2676
|
+
### Staging
|
|
2677
|
+
- Production-like settings
|
|
2678
|
+
- Separate database
|
|
2679
|
+
- Full feature set enabled
|
|
2680
|
+
- Error reporting enabled
|
|
2681
|
+
|
|
2682
|
+
### Production
|
|
2683
|
+
- Optimized settings
|
|
2684
|
+
- Production database
|
|
2685
|
+
- Security hardening
|
|
2686
|
+
- Full monitoring enabled
|
|
2687
|
+
|
|
2688
|
+
## Configuration Validation
|
|
2689
|
+
|
|
2690
|
+
The service validates configuration on startup:
|
|
2691
|
+
|
|
2692
|
+
1. **Environment Variables**: Required variables present and valid
|
|
2693
|
+
2. **Service Configuration**: domains.js structure and values
|
|
2694
|
+
3. **Worker Configuration**: wrangler.toml syntax and bindings
|
|
2695
|
+
4. **Feature Compatibility**: Feature flags compatible with service type
|
|
2696
|
+
|
|
2697
|
+
## Runtime Configuration
|
|
2698
|
+
|
|
2699
|
+
Some configuration can be changed at runtime:
|
|
2700
|
+
|
|
2701
|
+
- Environment variables (require restart)
|
|
2702
|
+
- Feature flags (may require restart)
|
|
2703
|
+
- Database connections (handled automatically)
|
|
2704
|
+
- Logging levels (immediate effect)
|
|
2705
|
+
|
|
2706
|
+
## Security Considerations
|
|
2707
|
+
|
|
2708
|
+
- Never commit secrets to version control
|
|
2709
|
+
- Use Cloudflare Workers secrets for sensitive data
|
|
2710
|
+
- Rotate API tokens regularly
|
|
2711
|
+
- Limit feature access based on environment
|
|
2712
|
+
- Validate all input data
|
|
2713
|
+
- Use HTTPS for all production endpoints
|
|
2714
|
+
|
|
2715
|
+
## Troubleshooting Configuration Issues
|
|
2716
|
+
|
|
2717
|
+
### Common Problems
|
|
2718
|
+
|
|
2719
|
+
1. **Missing environment variables**
|
|
2720
|
+
- Check .env file exists and is loaded
|
|
2721
|
+
- Verify variable names match expectations
|
|
2722
|
+
|
|
2723
|
+
2. **Invalid Cloudflare credentials**
|
|
2724
|
+
- Check account ID format (32 hex characters)
|
|
2725
|
+
- Verify API token permissions
|
|
2726
|
+
- Confirm zone ID is correct
|
|
2727
|
+
|
|
2728
|
+
3. **Database connection issues**
|
|
2729
|
+
- Verify D1 database exists
|
|
2730
|
+
- Check database ID in wrangler.toml
|
|
2731
|
+
- Confirm database binding name
|
|
2732
|
+
|
|
2733
|
+
4. **Feature flag conflicts**
|
|
2734
|
+
- Some features require others to be enabled
|
|
2735
|
+
- Check service type compatibility
|
|
2736
|
+
|
|
2737
|
+
### Debugging Configuration
|
|
2738
|
+
|
|
2739
|
+
\`\`\`bash
|
|
2740
|
+
# Check environment variables
|
|
2741
|
+
node -e "console.log(process.env)"
|
|
2742
|
+
|
|
2743
|
+
# Validate service configuration
|
|
2744
|
+
node -e "import('./src/config/domains.js').then(config => console.log(JSON.stringify(config, null, 2)))"
|
|
2745
|
+
|
|
2746
|
+
# Test wrangler configuration
|
|
2747
|
+
wrangler dev --dry-run
|
|
2748
|
+
\`\`\`
|
|
2749
|
+
`;
|
|
2750
|
+
const filePath = join(servicePath, 'docs', 'CONFIGURATION.md');
|
|
2751
|
+
writeFileSync(filePath, configDocsContent, 'utf8');
|
|
2752
|
+
return filePath;
|
|
2753
|
+
}
|
|
2754
|
+
|
|
2755
|
+
/**
|
|
2756
|
+
* Generate CI workflow
|
|
2757
|
+
*/
|
|
2758
|
+
generateCiWorkflow(coreInputs, confirmedValues, servicePath) {
|
|
2759
|
+
const ciWorkflow = `name: CI
|
|
2760
|
+
|
|
2761
|
+
on:
|
|
2762
|
+
push:
|
|
2763
|
+
branches: [ main, master ]
|
|
2764
|
+
pull_request:
|
|
2765
|
+
branches: [ main, master ]
|
|
2766
|
+
|
|
2767
|
+
jobs:
|
|
2768
|
+
test:
|
|
2769
|
+
runs-on: ubuntu-latest
|
|
2770
|
+
|
|
2771
|
+
steps:
|
|
2772
|
+
- uses: actions/checkout@v4
|
|
2773
|
+
|
|
2774
|
+
- name: Setup Node.js
|
|
2775
|
+
uses: actions/setup-node@v4
|
|
2776
|
+
with:
|
|
2777
|
+
node-version: '18'
|
|
2778
|
+
cache: 'npm'
|
|
2779
|
+
|
|
2780
|
+
- name: Install dependencies
|
|
2781
|
+
run: npm ci
|
|
2782
|
+
|
|
2783
|
+
- name: Lint code
|
|
2784
|
+
run: npm run lint
|
|
2785
|
+
|
|
2786
|
+
- name: Run tests
|
|
2787
|
+
run: npm test
|
|
2788
|
+
|
|
2789
|
+
- name: Build
|
|
2790
|
+
run: npm run build
|
|
2791
|
+
|
|
2792
|
+
- name: Upload coverage reports
|
|
2793
|
+
uses: codecov/codecov-action@v3
|
|
2794
|
+
with:
|
|
2795
|
+
file: ./coverage/lcov.info
|
|
2796
|
+
`;
|
|
2797
|
+
const filePath = join(servicePath, '.github', 'workflows', 'ci.yml');
|
|
2798
|
+
writeFileSync(filePath, ciWorkflow, 'utf8');
|
|
2799
|
+
return filePath;
|
|
2800
|
+
}
|
|
2801
|
+
|
|
2802
|
+
/**
|
|
2803
|
+
* Generate deploy workflow
|
|
2804
|
+
*/
|
|
2805
|
+
generateDeployWorkflow(coreInputs, confirmedValues, servicePath) {
|
|
2806
|
+
const deployWorkflow = `name: Deploy
|
|
2807
|
+
|
|
2808
|
+
on:
|
|
2809
|
+
push:
|
|
2810
|
+
branches: [ main, master ]
|
|
2811
|
+
workflow_run:
|
|
2812
|
+
workflows: ["CI"]
|
|
2813
|
+
types:
|
|
2814
|
+
- completed
|
|
2815
|
+
|
|
2816
|
+
jobs:
|
|
2817
|
+
deploy-staging:
|
|
2818
|
+
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
|
|
2819
|
+
runs-on: ubuntu-latest
|
|
2820
|
+
environment: staging
|
|
2821
|
+
|
|
2822
|
+
steps:
|
|
2823
|
+
- uses: actions/checkout@v4
|
|
2824
|
+
|
|
2825
|
+
- name: Setup Node.js
|
|
2826
|
+
uses: actions/setup-node@v4
|
|
2827
|
+
with:
|
|
2828
|
+
node-version: '18'
|
|
2829
|
+
cache: 'npm'
|
|
2830
|
+
|
|
2831
|
+
- name: Install dependencies
|
|
2832
|
+
run: npm ci
|
|
2833
|
+
|
|
2834
|
+
- name: Deploy to staging
|
|
2835
|
+
run: npx wrangler deploy --env staging
|
|
2836
|
+
env:
|
|
2837
|
+
CLOUDFLARE_API_TOKEN: \${{ secrets.CLOUDFLARE_API_TOKEN }}
|
|
2838
|
+
CLOUDFLARE_ACCOUNT_ID: \${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
|
2839
|
+
|
|
2840
|
+
deploy-production:
|
|
2841
|
+
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
|
|
2842
|
+
runs-on: ubuntu-latest
|
|
2843
|
+
environment: production
|
|
2844
|
+
|
|
2845
|
+
steps:
|
|
2846
|
+
- uses: actions/checkout@v4
|
|
2847
|
+
|
|
2848
|
+
- name: Setup Node.js
|
|
2849
|
+
uses: actions/setup-node@v4
|
|
2850
|
+
with:
|
|
2851
|
+
node-version: '18'
|
|
2852
|
+
cache: 'npm'
|
|
2853
|
+
|
|
2854
|
+
- name: Install dependencies
|
|
2855
|
+
run: npm ci
|
|
2856
|
+
|
|
2857
|
+
- name: Deploy to production
|
|
2858
|
+
run: npx wrangler deploy --env production
|
|
2859
|
+
env:
|
|
2860
|
+
CLOUDFLARE_API_TOKEN: \${{ secrets.CLOUDFLARE_API_TOKEN }}
|
|
2861
|
+
CLOUDFLARE_ACCOUNT_ID: \${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
|
2862
|
+
`;
|
|
2863
|
+
const filePath = join(servicePath, '.github', 'workflows', 'deploy.yml');
|
|
2864
|
+
writeFileSync(filePath, deployWorkflow, 'utf8');
|
|
2865
|
+
return filePath;
|
|
2866
|
+
}
|
|
2867
|
+
|
|
2868
|
+
/**
|
|
2869
|
+
* Generate .gitignore
|
|
2870
|
+
*/
|
|
2871
|
+
generateGitignore(coreInputs, confirmedValues, servicePath) {
|
|
2872
|
+
const gitignoreContent = `# Dependencies
|
|
2873
|
+
node_modules/
|
|
2874
|
+
npm-debug.log*
|
|
2875
|
+
yarn-debug.log*
|
|
2876
|
+
yarn-error.log*
|
|
2877
|
+
|
|
2878
|
+
# Environment variables
|
|
2879
|
+
.env
|
|
2880
|
+
.env.local
|
|
2881
|
+
.env.development.local
|
|
2882
|
+
.env.test.local
|
|
2883
|
+
.env.production.local
|
|
2884
|
+
|
|
2885
|
+
# Build outputs
|
|
2886
|
+
dist/
|
|
2887
|
+
build/
|
|
2888
|
+
coverage/
|
|
2889
|
+
|
|
2890
|
+
# Logs
|
|
2891
|
+
logs/
|
|
2892
|
+
*.log
|
|
2893
|
+
npm-debug.log*
|
|
2894
|
+
yarn-debug.log*
|
|
2895
|
+
yarn-error.log*
|
|
2896
|
+
lerna-debug.log*
|
|
2897
|
+
|
|
2898
|
+
# Runtime data
|
|
2899
|
+
pids/
|
|
2900
|
+
*.pid
|
|
2901
|
+
*.seed
|
|
2902
|
+
*.pid.lock
|
|
2903
|
+
|
|
2904
|
+
# Coverage directory used by tools like istanbul
|
|
2905
|
+
coverage/
|
|
2906
|
+
*.lcov
|
|
2907
|
+
|
|
2908
|
+
# nyc test coverage
|
|
2909
|
+
.nyc_output/
|
|
2910
|
+
|
|
2911
|
+
# Dependency directories
|
|
2912
|
+
jspm_packages/
|
|
2913
|
+
|
|
2914
|
+
# Optional npm cache directory
|
|
2915
|
+
.npm
|
|
2916
|
+
|
|
2917
|
+
# Optional eslint cache
|
|
2918
|
+
.eslintcache
|
|
2919
|
+
|
|
2920
|
+
# Microbundle cache
|
|
2921
|
+
.rpt2_cache/
|
|
2922
|
+
.rts2_cache_cjs/
|
|
2923
|
+
.rts2_cache_es/
|
|
2924
|
+
.rts2_cache_umd/
|
|
2925
|
+
|
|
2926
|
+
# Optional REPL history
|
|
2927
|
+
.node_repl_history
|
|
2928
|
+
|
|
2929
|
+
# Output of 'npm pack'
|
|
2930
|
+
*.tgz
|
|
2931
|
+
|
|
2932
|
+
# Yarn Integrity file
|
|
2933
|
+
.yarn-integrity
|
|
2934
|
+
|
|
2935
|
+
# parcel-bundler cache (https://parceljs.org/)
|
|
2936
|
+
.cache
|
|
2937
|
+
.parcel-cache
|
|
2938
|
+
|
|
2939
|
+
# Next.js build / generate output
|
|
2940
|
+
.next
|
|
2941
|
+
|
|
2942
|
+
# Nuxt.js build / generate output
|
|
2943
|
+
.nuxt
|
|
2944
|
+
dist
|
|
2945
|
+
|
|
2946
|
+
# Gatsby files
|
|
2947
|
+
.cache/
|
|
2948
|
+
public
|
|
2949
|
+
|
|
2950
|
+
# Storybook build outputs
|
|
2951
|
+
.out
|
|
2952
|
+
.storybook-out
|
|
2953
|
+
|
|
2954
|
+
# Temporary folders
|
|
2955
|
+
tmp/
|
|
2956
|
+
temp/
|
|
2957
|
+
|
|
2958
|
+
# Editor directories and files
|
|
2959
|
+
.vscode/*
|
|
2960
|
+
!.vscode/extensions.json
|
|
2961
|
+
.idea
|
|
2962
|
+
.DS_Store
|
|
2963
|
+
*.suo
|
|
2964
|
+
*.ntvs*
|
|
2965
|
+
*.njsproj
|
|
2966
|
+
*.sln
|
|
2967
|
+
*.sw?
|
|
2968
|
+
|
|
2969
|
+
# Cloudflare
|
|
2970
|
+
.wrangler/
|
|
2971
|
+
`;
|
|
2972
|
+
const filePath = join(servicePath, '.gitignore');
|
|
2973
|
+
writeFileSync(filePath, gitignoreContent, 'utf8');
|
|
2974
|
+
return filePath;
|
|
2975
|
+
}
|
|
2976
|
+
|
|
2977
|
+
/**
|
|
2978
|
+
* Generate docker-compose.yml
|
|
2979
|
+
*/
|
|
2980
|
+
generateDockerCompose(coreInputs, confirmedValues, servicePath) {
|
|
2981
|
+
const dockerComposeContent = `version: '3.8'
|
|
2982
|
+
|
|
2983
|
+
services:
|
|
2984
|
+
${coreInputs.serviceName}:
|
|
2985
|
+
build:
|
|
2986
|
+
context: .
|
|
2987
|
+
dockerfile: Dockerfile
|
|
2988
|
+
ports:
|
|
2989
|
+
- "8787:8787"
|
|
2990
|
+
environment:
|
|
2991
|
+
- NODE_ENV=development
|
|
2992
|
+
- SERVICE_NAME=${coreInputs.serviceName}
|
|
2993
|
+
- SERVICE_TYPE=${coreInputs.serviceType}
|
|
2994
|
+
- ENVIRONMENT=development
|
|
2995
|
+
volumes:
|
|
2996
|
+
- .:/app
|
|
2997
|
+
- /app/node_modules
|
|
2998
|
+
command: npm run dev
|
|
2999
|
+
|
|
3000
|
+
${coreInputs.serviceName}-db:
|
|
3001
|
+
image: cloudflare/d1
|
|
3002
|
+
ports:
|
|
3003
|
+
- "8788:8787"
|
|
3004
|
+
environment:
|
|
3005
|
+
- DATABASE_NAME=${confirmedValues.databaseName}
|
|
3006
|
+
volumes:
|
|
3007
|
+
- .wrangler:/data
|
|
3008
|
+
`;
|
|
3009
|
+
const filePath = join(servicePath, 'docker-compose.yml');
|
|
3010
|
+
writeFileSync(filePath, dockerComposeContent, 'utf8');
|
|
3011
|
+
return filePath;
|
|
3012
|
+
}
|
|
3013
|
+
|
|
3014
|
+
/**
|
|
3015
|
+
* Create service manifest
|
|
3016
|
+
*/
|
|
3017
|
+
createServiceManifest(coreInputs, confirmedValues, generatedFiles) {
|
|
3018
|
+
return {
|
|
3019
|
+
manifestVersion: '1.0.0',
|
|
3020
|
+
frameworkVersion: '3.0.0',
|
|
3021
|
+
generatedAt: new Date().toISOString(),
|
|
3022
|
+
service: {
|
|
3023
|
+
name: coreInputs.serviceName,
|
|
3024
|
+
displayName: confirmedValues.displayName,
|
|
3025
|
+
description: confirmedValues.description,
|
|
3026
|
+
type: coreInputs.serviceType,
|
|
3027
|
+
version: confirmedValues.version,
|
|
3028
|
+
author: confirmedValues.author
|
|
3029
|
+
},
|
|
3030
|
+
configuration: {
|
|
3031
|
+
coreInputs,
|
|
3032
|
+
confirmedValues,
|
|
3033
|
+
urls: {
|
|
3034
|
+
production: confirmedValues.productionUrl,
|
|
3035
|
+
staging: confirmedValues.stagingUrl,
|
|
3036
|
+
development: confirmedValues.developmentUrl,
|
|
3037
|
+
documentation: confirmedValues.documentationUrl
|
|
3038
|
+
},
|
|
3039
|
+
api: {
|
|
3040
|
+
basePath: confirmedValues.apiBasePath,
|
|
3041
|
+
healthCheckPath: confirmedValues.healthCheckPath
|
|
3042
|
+
},
|
|
3043
|
+
cloudflare: {
|
|
3044
|
+
accountId: coreInputs.cloudflareAccountId,
|
|
3045
|
+
zoneId: coreInputs.cloudflareZoneId,
|
|
3046
|
+
workerName: confirmedValues.workerName,
|
|
3047
|
+
databaseName: confirmedValues.databaseName
|
|
3048
|
+
},
|
|
3049
|
+
features: confirmedValues.features
|
|
3050
|
+
},
|
|
3051
|
+
files: {
|
|
3052
|
+
total: generatedFiles.length,
|
|
3053
|
+
list: generatedFiles.map(file => relative(process.cwd(), file)),
|
|
3054
|
+
byCategory: this.categorizeFiles(generatedFiles)
|
|
3055
|
+
},
|
|
3056
|
+
metadata: {
|
|
3057
|
+
generationEngine: 'GenerationEngine v1.0.0',
|
|
3058
|
+
tier: 'Tier 3 - Automated Generation',
|
|
3059
|
+
checksum: this.generateChecksum(generatedFiles)
|
|
3060
|
+
}
|
|
3061
|
+
};
|
|
3062
|
+
}
|
|
3063
|
+
|
|
3064
|
+
/**
|
|
3065
|
+
* Categorize generated files
|
|
3066
|
+
*/
|
|
3067
|
+
categorizeFiles(files) {
|
|
3068
|
+
const categories = {
|
|
3069
|
+
core: [],
|
|
3070
|
+
service: [],
|
|
3071
|
+
environment: [],
|
|
3072
|
+
testing: [],
|
|
3073
|
+
documentation: [],
|
|
3074
|
+
automation: []
|
|
3075
|
+
};
|
|
3076
|
+
files.forEach(file => {
|
|
3077
|
+
const relativePath = relative(process.cwd(), file);
|
|
3078
|
+
if (relativePath.includes('package.json') || relativePath.includes('wrangler.toml') || relativePath.includes('.env')) {
|
|
3079
|
+
categories.core.push(relativePath);
|
|
3080
|
+
} else if (relativePath.includes('src/')) {
|
|
3081
|
+
categories.service.push(relativePath);
|
|
3082
|
+
} else if (relativePath.includes('config/') || relativePath.includes('scripts/')) {
|
|
3083
|
+
categories.environment.push(relativePath);
|
|
3084
|
+
} else if (relativePath.includes('test/') || relativePath.includes('jest.config.js') || relativePath.includes('.eslintrc.js')) {
|
|
3085
|
+
categories.testing.push(relativePath);
|
|
3086
|
+
} else if (relativePath.includes('docs/') || relativePath.includes('README.md')) {
|
|
3087
|
+
categories.documentation.push(relativePath);
|
|
3088
|
+
} else if (relativePath.includes('.github/') || relativePath.includes('.gitignore') || relativePath.includes('docker-compose.yml')) {
|
|
3089
|
+
categories.automation.push(relativePath);
|
|
3090
|
+
}
|
|
3091
|
+
});
|
|
3092
|
+
return categories;
|
|
3093
|
+
}
|
|
3094
|
+
|
|
3095
|
+
/**
|
|
3096
|
+
* Generate checksum for verification
|
|
3097
|
+
*/
|
|
3098
|
+
generateChecksum(files) {
|
|
3099
|
+
// Simple checksum based on file count and names
|
|
3100
|
+
const fileString = files.map(f => relative(process.cwd(), f)).sort().join('');
|
|
3101
|
+
let hash = 0;
|
|
3102
|
+
for (let i = 0; i < fileString.length; i++) {
|
|
3103
|
+
const char = fileString.charCodeAt(i);
|
|
3104
|
+
hash = (hash << 5) - hash + char;
|
|
3105
|
+
hash = hash & hash; // Convert to 32-bit integer
|
|
3106
|
+
}
|
|
3107
|
+
return Math.abs(hash).toString(16);
|
|
3108
|
+
}
|
|
3109
|
+
}
|