@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,889 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Clodo Framework - Service Initializer
|
|
5
|
+
*
|
|
6
|
+
* Initializes a new service with auto-generated configurations,
|
|
7
|
+
* eliminating the need for manual wrangler.toml and domains.js setup.
|
|
8
|
+
*
|
|
9
|
+
* This solves the workflow order problem by making configuration
|
|
10
|
+
* generation the first step, not a prerequisite.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { program } from 'commander';
|
|
14
|
+
import { ServiceInitializer } from '../../dist/service-management/ServiceInitializer.js';
|
|
15
|
+
|
|
16
|
+
const SERVICE_TYPES = ['generic', 'data-service', 'auth-service', 'content-service', 'api-gateway'];
|
|
17
|
+
|
|
18
|
+
class ServiceInitializerCLI {
|
|
19
|
+
constructor() {
|
|
20
|
+
this.setupCLI();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
setupCLI() {
|
|
24
|
+
program
|
|
25
|
+
.name('clodo-init-service')
|
|
26
|
+
.description('Initialize a Clodo Framework service with auto-generated configurations')
|
|
27
|
+
.version('1.0.0')
|
|
28
|
+
.argument('<service-name>', 'Name of the service to initialize')
|
|
29
|
+
.option('-t, --type <type>', 'Service type', 'generic')
|
|
30
|
+
.option('-d, --domains <domains>', 'Comma-separated list of domains (can include account info)')
|
|
31
|
+
.option('-e, --env <environment>', 'Target environment', 'development')
|
|
32
|
+
.option('--api-token <token>', 'Cloudflare API token for domain discovery')
|
|
33
|
+
.option('--account-id <id>', 'Default Cloudflare account ID')
|
|
34
|
+
.option('--zone-id <id>', 'Default Cloudflare zone ID')
|
|
35
|
+
.option('-o, --output <path>', 'Output directory (services will be created in a services/ subdirectory)', process.cwd())
|
|
36
|
+
.option('-f, --force', 'Overwrite existing service directory')
|
|
37
|
+
.option('--dry-run', 'Show what would be created without creating files')
|
|
38
|
+
.option('--multi-domain', 'Generate configurations for multiple domains')
|
|
39
|
+
.action((serviceName, options) => {
|
|
40
|
+
this.initializeService(serviceName, options);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
program.on('--help', () => {
|
|
44
|
+
console.log('\nExamples:');
|
|
45
|
+
console.log(' clodo-init-service my-api --type api-gateway --domains "api.example.com,staging.example.com"');
|
|
46
|
+
console.log(' clodo-init-service data-service --type data-service --api-token $CF_TOKEN');
|
|
47
|
+
console.log(' clodo-init-service my-service --env production --account-id $CF_ACCOUNT_ID');
|
|
48
|
+
console.log('\nServices are created in: ./services/{service-name}/');
|
|
49
|
+
console.log('\nEnvironment Variables:');
|
|
50
|
+
console.log(' CLOUDFLARE_API_TOKEN - API token for domain discovery');
|
|
51
|
+
console.log(' CLOUDFLARE_ACCOUNT_ID - Account ID for configurations');
|
|
52
|
+
console.log(' CLOUDFLARE_ZONE_ID - Zone ID for domain configurations');
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async initializeService(serviceName, options) {
|
|
57
|
+
try {
|
|
58
|
+
console.log('🚀 Clodo Framework - Service Initializer');
|
|
59
|
+
console.log('=' .repeat(50));
|
|
60
|
+
console.log('📦 Using Clodo Framework ServiceInitializer module');
|
|
61
|
+
console.log('');
|
|
62
|
+
|
|
63
|
+
const initializer = new ServiceInitializer();
|
|
64
|
+
const result = await initializer.initializeService(serviceName, {
|
|
65
|
+
type: options.type,
|
|
66
|
+
domains: options.domains,
|
|
67
|
+
env: options.env,
|
|
68
|
+
apiToken: options.apiToken,
|
|
69
|
+
accountId: options.accountId,
|
|
70
|
+
zoneId: options.zoneId,
|
|
71
|
+
output: options.output,
|
|
72
|
+
force: options.force,
|
|
73
|
+
dryRun: options.dryRun
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
if (result.success) {
|
|
77
|
+
if (result.dryRun) {
|
|
78
|
+
console.log('📋 Dry run - would create the following:');
|
|
79
|
+
console.log('Files:', result.configs.join(', '));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
console.log('\n✅ Service initialized successfully!');
|
|
84
|
+
console.log('\n📝 Next steps:');
|
|
85
|
+
console.log(` cd services/${serviceName}`);
|
|
86
|
+
console.log(' npm install');
|
|
87
|
+
console.log(' npm run dev # Start development server');
|
|
88
|
+
console.log(' npm run deploy # Deploy to Cloudflare');
|
|
89
|
+
} else {
|
|
90
|
+
console.error('\n❌ Initialization failed:', result.error);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.error('\n❌ Unexpected error:', error.message);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Run the CLI
|
|
101
|
+
const cli = new ServiceInitializerCLI();
|
|
102
|
+
program.parse();
|
|
103
|
+
|
|
104
|
+
// Discover domains if API token provided
|
|
105
|
+
const domainInfo = await this.discoverDomains(options, serviceName);
|
|
106
|
+
|
|
107
|
+
// Generate configurations
|
|
108
|
+
const configs = this.generateConfigurations(serviceName, options, domainInfo);
|
|
109
|
+
|
|
110
|
+
if (options.dryRun) {
|
|
111
|
+
this.showDryRun(configs);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Create service structure
|
|
116
|
+
this.createServiceStructure(serviceName, options, configs);
|
|
117
|
+
|
|
118
|
+
// Validate setup
|
|
119
|
+
this.validateSetup(serviceName, options);
|
|
120
|
+
|
|
121
|
+
console.log('\n✅ Service initialized successfully!');
|
|
122
|
+
console.log('\n📝 Next steps:');
|
|
123
|
+
console.log(` cd services/${serviceName}`);
|
|
124
|
+
console.log(' npm install');
|
|
125
|
+
console.log(' npm run dev # Start development server');
|
|
126
|
+
console.log(' npm run deploy # Deploy to Cloudflare');
|
|
127
|
+
|
|
128
|
+
} catch (error) {
|
|
129
|
+
console.error('\n❌ Initialization failed:', error.message);
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
validateInputs(serviceName, options) {
|
|
135
|
+
// Validate service name
|
|
136
|
+
if (!/^[a-z0-9-]+$/.test(serviceName)) {
|
|
137
|
+
throw new Error('Service name must contain only lowercase letters, numbers, and hyphens');
|
|
138
|
+
}
|
|
139
|
+
if (serviceName.length < 3 || serviceName.length > 50) {
|
|
140
|
+
throw new Error('Service name must be between 3 and 50 characters');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Validate service type
|
|
144
|
+
if (!SERVICE_TYPES.includes(options.type)) {
|
|
145
|
+
throw new Error(`Invalid service type. Must be one of: ${SERVICE_TYPES.join(', ')}`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Check if service directory exists
|
|
149
|
+
const servicesDir = join(options.output, 'services');
|
|
150
|
+
const serviceDir = join(servicesDir, serviceName);
|
|
151
|
+
if (existsSync(serviceDir) && !options.force) {
|
|
152
|
+
throw new Error(`Service directory already exists: ${serviceDir}. Use --force to overwrite.`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async discoverDomains(options, serviceName) {
|
|
157
|
+
const domainInfo = {
|
|
158
|
+
domains: [],
|
|
159
|
+
defaultAccountId: options.accountId || process.env.CLOUDFLARE_ACCOUNT_ID,
|
|
160
|
+
defaultZoneId: options.zoneId || process.env.CLOUDFLARE_ZONE_ID,
|
|
161
|
+
apiToken: options.apiToken || process.env.CLOUDFLARE_API_TOKEN
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// Parse domains from command line - support format: domain[:accountId[:zoneId]]
|
|
165
|
+
if (options.domains) {
|
|
166
|
+
const domainSpecs = options.domains.split(',').map(d => d.trim());
|
|
167
|
+
domainInfo.domains = domainSpecs.map(spec => {
|
|
168
|
+
const parts = spec.split(':');
|
|
169
|
+
return {
|
|
170
|
+
domain: parts[0],
|
|
171
|
+
accountId: parts[1] || domainInfo.defaultAccountId,
|
|
172
|
+
zoneId: parts[2] || domainInfo.defaultZoneId,
|
|
173
|
+
name: `${serviceName}-${parts[0].replace(/[^a-zA-Z0-9]/g, '-')}`
|
|
174
|
+
};
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// If API token provided and no domains specified, try to discover
|
|
179
|
+
if (domainInfo.apiToken && !options.domains) {
|
|
180
|
+
console.log('🔍 Discovering domains from Cloudflare...');
|
|
181
|
+
try {
|
|
182
|
+
// Mock discovery for now - would integrate with real API
|
|
183
|
+
domainInfo.domains = [
|
|
184
|
+
{
|
|
185
|
+
domain: `${options.env === 'production' ? '' : options.env + '-'}api.example.com`,
|
|
186
|
+
accountId: domainInfo.defaultAccountId,
|
|
187
|
+
zoneId: domainInfo.defaultZoneId,
|
|
188
|
+
name: `${serviceName}-api`
|
|
189
|
+
}
|
|
190
|
+
];
|
|
191
|
+
console.log(` Found domain: ${domainInfo.domains[0].domain}`);
|
|
192
|
+
} catch (error) {
|
|
193
|
+
console.warn('⚠️ Domain discovery failed, using defaults:', error.message);
|
|
194
|
+
domainInfo.domains = [{
|
|
195
|
+
domain: `api.example.com`,
|
|
196
|
+
accountId: domainInfo.defaultAccountId,
|
|
197
|
+
zoneId: domainInfo.defaultZoneId,
|
|
198
|
+
name: `${options.serviceName}-api`
|
|
199
|
+
}];
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Ensure we have at least one domain
|
|
204
|
+
if (domainInfo.domains.length === 0) {
|
|
205
|
+
domainInfo.domains = [{
|
|
206
|
+
domain: `api.example.com`,
|
|
207
|
+
accountId: domainInfo.defaultAccountId,
|
|
208
|
+
zoneId: domainInfo.defaultZoneId,
|
|
209
|
+
name: `${serviceName}-api`
|
|
210
|
+
}];
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return domainInfo;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
generateConfigurations(serviceName, options, domainInfo) {
|
|
217
|
+
console.log('🔧 Generating configurations...');
|
|
218
|
+
|
|
219
|
+
const configs = {};
|
|
220
|
+
|
|
221
|
+
// Generate wrangler.toml
|
|
222
|
+
configs.wrangler = this.generateWranglerConfig(serviceName, options, domainInfo);
|
|
223
|
+
|
|
224
|
+
// Generate domains.js
|
|
225
|
+
configs.domains = this.generateDomainsConfig(serviceName, options, domainInfo);
|
|
226
|
+
|
|
227
|
+
// Generate package.json
|
|
228
|
+
configs.package = this.generatePackageConfig(serviceName, options);
|
|
229
|
+
|
|
230
|
+
return configs;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
generateWranglerConfig(serviceName, options, domainInfo) {
|
|
234
|
+
const env = options.env;
|
|
235
|
+
|
|
236
|
+
// For multiple domains, generate separate wrangler files
|
|
237
|
+
if (domainInfo.domains.length > 1) {
|
|
238
|
+
const wranglerConfigs = {};
|
|
239
|
+
domainInfo.domains.forEach(domainConfig => {
|
|
240
|
+
const configName = `wrangler.${domainConfig.name}.toml`;
|
|
241
|
+
wranglerConfigs[configName] = this.generateSingleWranglerConfig(
|
|
242
|
+
domainConfig.name,
|
|
243
|
+
env,
|
|
244
|
+
options,
|
|
245
|
+
domainConfig,
|
|
246
|
+
serviceName
|
|
247
|
+
);
|
|
248
|
+
});
|
|
249
|
+
return wranglerConfigs;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// For single domain, generate single wrangler.toml
|
|
253
|
+
return {
|
|
254
|
+
'wrangler.toml': this.generateSingleWranglerConfig(
|
|
255
|
+
`${serviceName}-${env}`,
|
|
256
|
+
env,
|
|
257
|
+
options,
|
|
258
|
+
domainInfo.domains[0],
|
|
259
|
+
serviceName
|
|
260
|
+
)
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
generateSingleWranglerConfig(name, env, options, domainConfig, serviceName) {
|
|
265
|
+
const isProduction = env === 'production';
|
|
266
|
+
|
|
267
|
+
let config = `name = "${name}"
|
|
268
|
+
main = "src/worker/index.js"
|
|
269
|
+
compatibility_date = "${new Date().toISOString().split('T')[0]}"
|
|
270
|
+
|
|
271
|
+
`;
|
|
272
|
+
|
|
273
|
+
if (!isProduction) {
|
|
274
|
+
config += `[env.${env}]
|
|
275
|
+
name = "${name}"
|
|
276
|
+
|
|
277
|
+
`;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
config += `[env.production]
|
|
281
|
+
name = "${name.replace(`-${env}`, '')}-production"
|
|
282
|
+
|
|
283
|
+
`;
|
|
284
|
+
|
|
285
|
+
// Add D1 databases for data services
|
|
286
|
+
if (options.type === 'data-service') {
|
|
287
|
+
config += `# Database bindings
|
|
288
|
+
[[d1_databases]]
|
|
289
|
+
binding = "${name.toUpperCase().replace(/[^A-Z0-9]/g, '_')}_DB"
|
|
290
|
+
database_name = "${name}-db"
|
|
291
|
+
|
|
292
|
+
`;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Environment variables
|
|
296
|
+
config += `# Environment variables
|
|
297
|
+
[vars]
|
|
298
|
+
DOMAIN_NAME = "${domainConfig.domain}"
|
|
299
|
+
ENVIRONMENT = "${env}"
|
|
300
|
+
SERVICE_NAME = "${serviceName}"
|
|
301
|
+
SERVICE_TYPE = "${options.type}"
|
|
302
|
+
DOMAIN_CONFIG = "${domainConfig.name}"
|
|
303
|
+
|
|
304
|
+
`;
|
|
305
|
+
|
|
306
|
+
// Add service-specific environment variables
|
|
307
|
+
if (options.type === 'auth-service') {
|
|
308
|
+
config += `JWT_SECRET = "change-in-production"
|
|
309
|
+
AUTH_PROVIDERS = "google,github"
|
|
310
|
+
`;
|
|
311
|
+
} else if (options.type === 'api-gateway') {
|
|
312
|
+
config += `RATE_LIMIT_REQUESTS = "100"
|
|
313
|
+
RATE_LIMIT_WINDOW_MS = "60000"
|
|
314
|
+
ENABLE_CORS = "true"
|
|
315
|
+
`;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return config;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
generateDomainsConfig(serviceName, options, domainInfo) {
|
|
322
|
+
let config = `import { createDomainConfigSchema } from '@tamyla/clodo-framework';
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Domain configurations for ${serviceName}
|
|
326
|
+
*
|
|
327
|
+
* Auto-generated by Clodo Framework Service Initializer
|
|
328
|
+
* Generated on: ${new Date().toISOString()}
|
|
329
|
+
*
|
|
330
|
+
* This service supports multiple domains with domain-specific configurations
|
|
331
|
+
*/
|
|
332
|
+
|
|
333
|
+
export const domains = {
|
|
334
|
+
`;
|
|
335
|
+
|
|
336
|
+
domainInfo.domains.forEach((domainConfig, index) => {
|
|
337
|
+
const isLast = index === domainInfo.domains.length - 1;
|
|
338
|
+
config += this.generateSingleDomainConfig(serviceName, options, domainConfig, isLast);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
config += `};
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Get domain configuration by domain name
|
|
345
|
+
*/
|
|
346
|
+
export function getDomainConfig(domainName) {
|
|
347
|
+
// Try exact match first
|
|
348
|
+
if (domains[domainName]) {
|
|
349
|
+
return domains[domainName];
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Try to find by domain (for runtime domain detection)
|
|
353
|
+
for (const [key, config] of Object.entries(domains)) {
|
|
354
|
+
if (config.domains.production === domainName ||
|
|
355
|
+
config.domains.staging === domainName ||
|
|
356
|
+
config.domains.development === domainName) {
|
|
357
|
+
return config;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Fallback to first domain
|
|
362
|
+
const firstDomain = Object.keys(domains)[0];
|
|
363
|
+
console.warn(\`Domain \${domainName} not found, using \${firstDomain}\`);
|
|
364
|
+
return domains[firstDomain];
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Get all available domains
|
|
369
|
+
*/
|
|
370
|
+
export function getAvailableDomains() {
|
|
371
|
+
return Object.keys(domains);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Get domain configuration for current environment
|
|
376
|
+
*/
|
|
377
|
+
export function getCurrentDomainConfig() {
|
|
378
|
+
// This would be determined by the request hostname in the worker
|
|
379
|
+
// For now, return the first domain
|
|
380
|
+
const firstDomain = Object.keys(domains)[0];
|
|
381
|
+
return domains[firstDomain];
|
|
382
|
+
}
|
|
383
|
+
`;
|
|
384
|
+
|
|
385
|
+
return config;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
generateSingleDomainConfig(serviceName, options, domainConfig, isLast) {
|
|
389
|
+
const env = options.env;
|
|
390
|
+
|
|
391
|
+
let config = ` '${domainConfig.name}': {
|
|
392
|
+
...createDomainConfigSchema(),
|
|
393
|
+
name: '${domainConfig.name}',
|
|
394
|
+
displayName: '${this.toTitleCase(domainConfig.name.replace(/-/g, ' '))}',
|
|
395
|
+
accountId: '${domainConfig.accountId || 'YOUR_CLOUDFLARE_ACCOUNT_ID'}',
|
|
396
|
+
zoneId: '${domainConfig.zoneId || 'YOUR_CLOUDFLARE_ZONE_ID'}',
|
|
397
|
+
domains: {
|
|
398
|
+
production: '${domainConfig.domain}',
|
|
399
|
+
staging: 'staging-${domainConfig.domain}',
|
|
400
|
+
development: 'dev-${domainConfig.domain}'
|
|
401
|
+
},
|
|
402
|
+
services: [
|
|
403
|
+
'${serviceName}'
|
|
404
|
+
],
|
|
405
|
+
`;
|
|
406
|
+
|
|
407
|
+
// Add service-specific databases
|
|
408
|
+
if (options.type === 'data-service') {
|
|
409
|
+
config += `
|
|
410
|
+
databases: [
|
|
411
|
+
{
|
|
412
|
+
name: '${domainConfig.name}-db',
|
|
413
|
+
type: 'd1'
|
|
414
|
+
}
|
|
415
|
+
],`;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Add features based on service type
|
|
419
|
+
config += `
|
|
420
|
+
features: {
|
|
421
|
+
// Core features (always enabled)
|
|
422
|
+
logging: true,
|
|
423
|
+
monitoring: true,
|
|
424
|
+
errorReporting: true,
|
|
425
|
+
`;
|
|
426
|
+
|
|
427
|
+
// Service-specific features
|
|
428
|
+
if (options.type === 'data-service') {
|
|
429
|
+
config += `
|
|
430
|
+
// Data service features
|
|
431
|
+
authentication: true,
|
|
432
|
+
authorization: true,
|
|
433
|
+
fileStorage: false,
|
|
434
|
+
search: true,
|
|
435
|
+
filtering: true,
|
|
436
|
+
pagination: true,
|
|
437
|
+
`;
|
|
438
|
+
} else if (options.type === 'auth-service') {
|
|
439
|
+
config += `
|
|
440
|
+
// Auth service features
|
|
441
|
+
authentication: true,
|
|
442
|
+
authorization: true,
|
|
443
|
+
userProfiles: true,
|
|
444
|
+
emailNotifications: true,
|
|
445
|
+
magicLinkAuth: true,
|
|
446
|
+
`;
|
|
447
|
+
} else if (options.type === 'api-gateway') {
|
|
448
|
+
config += `
|
|
449
|
+
// API Gateway features
|
|
450
|
+
authentication: false,
|
|
451
|
+
authorization: false,
|
|
452
|
+
rateLimiting: true,
|
|
453
|
+
caching: true,
|
|
454
|
+
monitoring: true,
|
|
455
|
+
`;
|
|
456
|
+
} else if (options.type === 'content-service') {
|
|
457
|
+
config += `
|
|
458
|
+
// Content service features
|
|
459
|
+
fileStorage: true,
|
|
460
|
+
search: true,
|
|
461
|
+
filtering: true,
|
|
462
|
+
pagination: true,
|
|
463
|
+
caching: true,
|
|
464
|
+
`;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
config += `
|
|
468
|
+
// Domain-specific customizations
|
|
469
|
+
domainCustomizations: true,
|
|
470
|
+
},
|
|
471
|
+
settings: {
|
|
472
|
+
environment: '${env}',
|
|
473
|
+
logLevel: 'info',
|
|
474
|
+
corsOrigins: ['https://${domainConfig.domain}'], // Domain-specific CORS
|
|
475
|
+
rateLimitRequests: 100,
|
|
476
|
+
rateLimitWindowMs: 60000,
|
|
477
|
+
enableMetrics: true,
|
|
478
|
+
metricsEndpoint: '/metrics',
|
|
479
|
+
domainName: '${domainConfig.domain}'
|
|
480
|
+
}
|
|
481
|
+
}`;
|
|
482
|
+
|
|
483
|
+
if (!isLast) {
|
|
484
|
+
config += ',';
|
|
485
|
+
}
|
|
486
|
+
config += '\n';
|
|
487
|
+
|
|
488
|
+
return config;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
generatePackageConfig(serviceName, options) {
|
|
492
|
+
const config = {
|
|
493
|
+
name: serviceName,
|
|
494
|
+
version: '1.0.0',
|
|
495
|
+
description: `${this.toTitleCase(serviceName.replace(/-/g, ' '))} - ${options.type} service`,
|
|
496
|
+
type: 'module',
|
|
497
|
+
main: 'src/worker/index.js',
|
|
498
|
+
scripts: {
|
|
499
|
+
build: 'babel src/ --out-dir dist/',
|
|
500
|
+
dev: 'wrangler dev',
|
|
501
|
+
deploy: 'wrangler deploy',
|
|
502
|
+
test: 'jest',
|
|
503
|
+
lint: 'eslint src',
|
|
504
|
+
'lint:fix': 'eslint src --fix'
|
|
505
|
+
},
|
|
506
|
+
dependencies: {
|
|
507
|
+
'@tamyla/clodo-framework': 'file:../'
|
|
508
|
+
},
|
|
509
|
+
devDependencies: {
|
|
510
|
+
wrangler: '^3.0.0',
|
|
511
|
+
jest: '^29.0.0',
|
|
512
|
+
eslint: '^8.0.0',
|
|
513
|
+
'@babel/cli': '^7.0.0',
|
|
514
|
+
'@babel/core': '^7.0.0',
|
|
515
|
+
'@babel/preset-env': '^7.0.0'
|
|
516
|
+
},
|
|
517
|
+
keywords: [
|
|
518
|
+
'cloudflare',
|
|
519
|
+
'workers',
|
|
520
|
+
'clodo-framework',
|
|
521
|
+
options.type
|
|
522
|
+
],
|
|
523
|
+
author: 'Generated by Clodo Framework',
|
|
524
|
+
license: 'MIT'
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
return JSON.stringify(config, null, 2);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
showDryRun(configs) {
|
|
531
|
+
console.log('\n📋 DRY RUN - Would create the following files:');
|
|
532
|
+
console.log('\n📄 wrangler.toml:');
|
|
533
|
+
console.log(configs.wrangler);
|
|
534
|
+
console.log('\n📄 src/config/domains.js:');
|
|
535
|
+
console.log(configs.domains);
|
|
536
|
+
console.log('\n📄 package.json:');
|
|
537
|
+
console.log(configs.package);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
createServiceStructure(serviceName, options, configs) {
|
|
541
|
+
const servicesDir = join(options.output, 'services');
|
|
542
|
+
const serviceDir = join(servicesDir, serviceName);
|
|
543
|
+
|
|
544
|
+
console.log(`📁 Creating service structure in: ${serviceDir}`);
|
|
545
|
+
|
|
546
|
+
// Create directories
|
|
547
|
+
const dirs = [
|
|
548
|
+
serviceDir,
|
|
549
|
+
join(serviceDir, 'src'),
|
|
550
|
+
join(serviceDir, 'src', 'worker'),
|
|
551
|
+
join(serviceDir, 'src', 'config'),
|
|
552
|
+
join(serviceDir, 'tests'),
|
|
553
|
+
join(serviceDir, 'docs')
|
|
554
|
+
];
|
|
555
|
+
|
|
556
|
+
for (const dir of dirs) {
|
|
557
|
+
if (!existsSync(dir)) {
|
|
558
|
+
mkdirSync(dir, { recursive: true });
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// Write wrangler configurations (could be single file or multiple)
|
|
563
|
+
if (typeof configs.wrangler === 'string') {
|
|
564
|
+
// Single wrangler.toml
|
|
565
|
+
writeFileSync(join(serviceDir, 'wrangler.toml'), configs.wrangler);
|
|
566
|
+
} else {
|
|
567
|
+
// Multiple wrangler files
|
|
568
|
+
for (const [filename, content] of Object.entries(configs.wrangler)) {
|
|
569
|
+
writeFileSync(join(serviceDir, filename), content);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// Write other configuration files
|
|
574
|
+
writeFileSync(join(serviceDir, 'package.json'), configs.package);
|
|
575
|
+
writeFileSync(join(serviceDir, 'src', 'config', 'domains.js'), configs.domains);
|
|
576
|
+
|
|
577
|
+
// Create basic worker file
|
|
578
|
+
this.createWorkerFile(serviceName, options, serviceDir);
|
|
579
|
+
|
|
580
|
+
// Create README
|
|
581
|
+
this.createReadmeFile(serviceName, options, serviceDir);
|
|
582
|
+
|
|
583
|
+
console.log('✅ Configuration files created');
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
createWorkerFile(serviceName, options, serviceDir) {
|
|
587
|
+
const workerContent = `import { initializeService, createFeatureGuard, COMMON_FEATURES } from '@tamyla/clodo-framework';
|
|
588
|
+
import { domains, getDomainConfig } from '../config/domains.js';
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* ${this.toTitleCase(serviceName.replace(/-/g, ' '))} - ${options.type} service
|
|
592
|
+
*
|
|
593
|
+
* Auto-generated by Clodo Framework Service Initializer
|
|
594
|
+
* Generated on: ${new Date().toISOString()}
|
|
595
|
+
*
|
|
596
|
+
* Supports multiple domains with domain-specific configurations
|
|
597
|
+
*/
|
|
598
|
+
|
|
599
|
+
export default {
|
|
600
|
+
async fetch(request, env, ctx) {
|
|
601
|
+
try {
|
|
602
|
+
// Get domain configuration based on request hostname
|
|
603
|
+
const hostname = new URL(request.url).hostname;
|
|
604
|
+
const domainConfig = getDomainConfig(hostname);
|
|
605
|
+
|
|
606
|
+
// Initialize service with domain-specific configuration
|
|
607
|
+
const service = initializeService(env, { [domainConfig.name]: domainConfig });
|
|
608
|
+
|
|
609
|
+
// Log request (if logging is enabled)
|
|
610
|
+
if (service.features.includes(COMMON_FEATURES.LOGGING)) {
|
|
611
|
+
console.log(\`\${request.method} \${request.url} - \${hostname} (\${service.environment}) [\${domainConfig.name}]\`);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
const url = new URL(request.url);
|
|
615
|
+
|
|
616
|
+
// Health check endpoint
|
|
617
|
+
if (url.pathname === '/health') {
|
|
618
|
+
return new Response(JSON.stringify({
|
|
619
|
+
status: 'healthy',
|
|
620
|
+
service: '${serviceName}',
|
|
621
|
+
version: '1.0.0',
|
|
622
|
+
type: '${options.type}',
|
|
623
|
+
domain: hostname,
|
|
624
|
+
domainConfig: domainConfig.name,
|
|
625
|
+
features: service.features,
|
|
626
|
+
environment: service.environment,
|
|
627
|
+
timestamp: new Date().toISOString()
|
|
628
|
+
}), {
|
|
629
|
+
headers: {
|
|
630
|
+
'Content-Type': 'application/json',
|
|
631
|
+
'Cache-Control': 'no-cache'
|
|
632
|
+
}
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// Domain info endpoint
|
|
637
|
+
if (url.pathname === '/domain') {
|
|
638
|
+
return new Response(JSON.stringify({
|
|
639
|
+
hostname: hostname,
|
|
640
|
+
domainConfig: domainConfig.name,
|
|
641
|
+
accountId: domainConfig.accountId,
|
|
642
|
+
zoneId: domainConfig.zoneId,
|
|
643
|
+
corsOrigins: domainConfig.settings.corsOrigins,
|
|
644
|
+
rateLimit: {
|
|
645
|
+
requests: domainConfig.settings.rateLimitRequests,
|
|
646
|
+
windowMs: domainConfig.settings.rateLimitWindowMs
|
|
647
|
+
}
|
|
648
|
+
}), {
|
|
649
|
+
headers: {
|
|
650
|
+
'Content-Type': 'application/json'
|
|
651
|
+
}
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// Service-specific endpoints
|
|
656
|
+
${this.getServiceSpecificEndpoints(options.type)}
|
|
657
|
+
|
|
658
|
+
// Default response
|
|
659
|
+
return new Response(JSON.stringify({
|
|
660
|
+
message: '${serviceName} service running',
|
|
661
|
+
type: '${options.type}',
|
|
662
|
+
domain: hostname,
|
|
663
|
+
domainConfig: domainConfig.name,
|
|
664
|
+
endpoints: ['/health', '/domain'${this.getServiceEndpoints(options.type)}]
|
|
665
|
+
}), {
|
|
666
|
+
headers: {
|
|
667
|
+
'Content-Type': 'application/json'
|
|
668
|
+
}
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
} catch (error) {
|
|
672
|
+
console.error('Service error:', error);
|
|
673
|
+
return new Response(JSON.stringify({
|
|
674
|
+
error: error.message,
|
|
675
|
+
status: 'error'
|
|
676
|
+
}), {
|
|
677
|
+
status: 500,
|
|
678
|
+
headers: {
|
|
679
|
+
'Content-Type': 'application/json'
|
|
680
|
+
}
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
};
|
|
685
|
+
`;
|
|
686
|
+
|
|
687
|
+
writeFileSync(join(serviceDir, 'src', 'worker', 'index.js'), workerContent);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
getServiceSpecificEndpoints(serviceType) {
|
|
691
|
+
const endpoints = {
|
|
692
|
+
'data-service': `
|
|
693
|
+
// Data service endpoints
|
|
694
|
+
if (url.pathname.startsWith('/api/data')) {
|
|
695
|
+
// Handle data operations
|
|
696
|
+
return new Response(JSON.stringify({
|
|
697
|
+
message: 'Data service endpoint',
|
|
698
|
+
path: url.pathname
|
|
699
|
+
}), {
|
|
700
|
+
headers: { 'Content-Type': 'application/json' }
|
|
701
|
+
});
|
|
702
|
+
}`,
|
|
703
|
+
'auth-service': `
|
|
704
|
+
// Auth service endpoints
|
|
705
|
+
if (url.pathname.startsWith('/auth')) {
|
|
706
|
+
// Handle authentication operations
|
|
707
|
+
return new Response(JSON.stringify({
|
|
708
|
+
message: 'Auth service endpoint',
|
|
709
|
+
path: url.pathname
|
|
710
|
+
}), {
|
|
711
|
+
headers: { 'Content-Type': 'application/json' }
|
|
712
|
+
});
|
|
713
|
+
}`,
|
|
714
|
+
'api-gateway': `
|
|
715
|
+
// API Gateway endpoints
|
|
716
|
+
if (url.pathname.startsWith('/api/')) {
|
|
717
|
+
// Route to appropriate services
|
|
718
|
+
return new Response(JSON.stringify({
|
|
719
|
+
message: 'API Gateway routing',
|
|
720
|
+
path: url.pathname
|
|
721
|
+
}), {
|
|
722
|
+
headers: { 'Content-Type': 'application/json' }
|
|
723
|
+
});
|
|
724
|
+
}`,
|
|
725
|
+
'content-service': `
|
|
726
|
+
// Content service endpoints
|
|
727
|
+
if (url.pathname.startsWith('/content')) {
|
|
728
|
+
// Handle content operations
|
|
729
|
+
return new Response(JSON.stringify({
|
|
730
|
+
message: 'Content service endpoint',
|
|
731
|
+
path: url.pathname
|
|
732
|
+
}), {
|
|
733
|
+
headers: { 'Content-Type': 'application/json' }
|
|
734
|
+
});
|
|
735
|
+
}`,
|
|
736
|
+
'generic': `
|
|
737
|
+
// Generic service endpoints
|
|
738
|
+
if (url.pathname.startsWith('/api/generic')) {
|
|
739
|
+
// Handle generic operations
|
|
740
|
+
return new Response(JSON.stringify({
|
|
741
|
+
message: 'Generic service endpoint',
|
|
742
|
+
path: url.pathname
|
|
743
|
+
}), {
|
|
744
|
+
headers: { 'Content-Type': 'application/json' }
|
|
745
|
+
});
|
|
746
|
+
}`
|
|
747
|
+
};
|
|
748
|
+
|
|
749
|
+
return endpoints[serviceType] || endpoints['generic'];
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
// Duplicate method removed - keeping the more comprehensive version below
|
|
753
|
+
|
|
754
|
+
getServiceEndpoints(serviceType) {
|
|
755
|
+
switch (serviceType) {
|
|
756
|
+
case 'data-service':
|
|
757
|
+
return ', \'/api/data\'';
|
|
758
|
+
case 'auth-service':
|
|
759
|
+
return ', \'/auth/login\'';
|
|
760
|
+
default:
|
|
761
|
+
return '';
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
createReadmeFile(serviceName, options, serviceDir) {
|
|
766
|
+
const readme = `# ${this.toTitleCase(serviceName.replace(/-/g, ' '))}
|
|
767
|
+
|
|
768
|
+
${options.type} service built with Clodo Framework.
|
|
769
|
+
|
|
770
|
+
## Setup
|
|
771
|
+
|
|
772
|
+
This service was auto-initialized with the following configuration:
|
|
773
|
+
|
|
774
|
+
- **Service Type**: ${options.type}
|
|
775
|
+
- **Environment**: ${options.env}
|
|
776
|
+
- **Framework**: Clodo Framework
|
|
777
|
+
|
|
778
|
+
## Quick Start
|
|
779
|
+
|
|
780
|
+
\`\`\`bash
|
|
781
|
+
npm install
|
|
782
|
+
npm run dev
|
|
783
|
+
\`\`\`
|
|
784
|
+
|
|
785
|
+
## Deployment
|
|
786
|
+
|
|
787
|
+
\`\`\`bash
|
|
788
|
+
npm run deploy
|
|
789
|
+
\`\`\`
|
|
790
|
+
|
|
791
|
+
## Configuration
|
|
792
|
+
|
|
793
|
+
### wrangler.toml
|
|
794
|
+
Contains Cloudflare Workers configuration with environment-specific settings.
|
|
795
|
+
|
|
796
|
+
### src/config/domains.js
|
|
797
|
+
Contains domain-specific configuration and feature flags.
|
|
798
|
+
|
|
799
|
+
## Environment Variables
|
|
800
|
+
|
|
801
|
+
Set these in your Cloudflare Workers environment or wrangler.toml:
|
|
802
|
+
|
|
803
|
+
- \`CLOUDFLARE_ACCOUNT_ID\` - Your Cloudflare account ID
|
|
804
|
+
- \`CLOUDFLARE_ZONE_ID\` - Your Cloudflare zone ID
|
|
805
|
+
|
|
806
|
+
## Development
|
|
807
|
+
|
|
808
|
+
\`\`\`bash
|
|
809
|
+
# Run tests
|
|
810
|
+
npm test
|
|
811
|
+
|
|
812
|
+
# Lint code
|
|
813
|
+
npm run lint
|
|
814
|
+
|
|
815
|
+
# Fix linting issues
|
|
816
|
+
npm run lint:fix
|
|
817
|
+
\`\`\`
|
|
818
|
+
|
|
819
|
+
## API Endpoints
|
|
820
|
+
|
|
821
|
+
- \`GET /health\` - Health check endpoint
|
|
822
|
+
${this.getApiDocs(options.type)}
|
|
823
|
+
|
|
824
|
+
---
|
|
825
|
+
|
|
826
|
+
*Generated by Clodo Framework Service Initializer on ${new Date().toISOString()}*
|
|
827
|
+
`;
|
|
828
|
+
|
|
829
|
+
writeFileSync(join(serviceDir, 'README.md'), readme);
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
getApiDocs(serviceType) {
|
|
833
|
+
switch (serviceType) {
|
|
834
|
+
case 'data-service':
|
|
835
|
+
return '- `GET/POST/PUT/DELETE /api/data` - Data operations';
|
|
836
|
+
case 'auth-service':
|
|
837
|
+
return '- `POST /auth/login` - User authentication\n- `POST /auth/logout` - User logout\n- `GET /auth/profile` - User profile';
|
|
838
|
+
case 'api-gateway':
|
|
839
|
+
return '- `/*` - API Gateway routes (configure in domains.js)';
|
|
840
|
+
case 'content-service':
|
|
841
|
+
return '- `GET /content/*` - Content delivery\n- `POST /content/upload` - Content upload';
|
|
842
|
+
default:
|
|
843
|
+
return '- Service-specific endpoints (configure as needed)';
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
validateSetup(serviceName, options) {
|
|
848
|
+
const servicesDir = join(options.output, 'services');
|
|
849
|
+
const serviceDir = join(servicesDir, serviceName);
|
|
850
|
+
|
|
851
|
+
console.log('🔍 Validating setup...');
|
|
852
|
+
|
|
853
|
+
const requiredFiles = [
|
|
854
|
+
'package.json',
|
|
855
|
+
'src/worker/index.js',
|
|
856
|
+
'src/config/domains.js',
|
|
857
|
+
'README.md'
|
|
858
|
+
];
|
|
859
|
+
|
|
860
|
+
// Check for wrangler files - either single or multiple
|
|
861
|
+
const wranglerFiles = readdirSync(serviceDir).filter(file => file.startsWith('wrangler.') && file.endsWith('.toml'));
|
|
862
|
+
if (wranglerFiles.length === 0 && !existsSync(join(serviceDir, 'wrangler.toml'))) {
|
|
863
|
+
throw new Error('No wrangler configuration files found');
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
for (const file of requiredFiles) {
|
|
867
|
+
const filePath = join(serviceDir, file);
|
|
868
|
+
if (!existsSync(filePath)) {
|
|
869
|
+
throw new Error(`Required file not created: ${file}`);
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
console.log('✅ All required files created successfully');
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
toTitleCase(str) {
|
|
877
|
+
return str.split(' ')
|
|
878
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
879
|
+
.join(' ');
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
run() {
|
|
883
|
+
program.parse();
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
// Run the initializer
|
|
888
|
+
const initializer = new ServiceInitializer();
|
|
889
|
+
initializer.run();
|