@tamyla/clodo-framework 2.0.18 → 2.0.20
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 +174 -88
- package/bin/clodo-service.js +31 -55
- package/dist/modules/security.js +24 -22
- package/dist/orchestration/multi-domain-orchestrator.js +33 -0
- package/dist/security/SecurityCLI.js +9 -66
- package/dist/security/index.js +7 -6
- package/dist/service-management/ServiceOrchestrator.js +14 -19
- package/dist/utils/config/unified-config-manager.js +448 -0
- package/dist/utils/deployment/index.js +1 -1
- package/dist/utils/deployment/wrangler-config-manager.js +363 -0
- package/package.json +4 -5
- package/bin/shared/config/customer-cli.js +0 -182
- package/dist/config/ConfigurationManager.js +0 -159
- package/dist/config/CustomerConfigCLI.js +0 -226
- package/dist/config/customer-config-loader.js +0 -247
- package/dist/security/DeploymentManager.js +0 -208
- package/dist/service-management/handlers/ConfigMutator.js +0 -130
- package/dist/shared/config/customer-cli.js +0 -175
- package/dist/utils/deployment/config-persistence.js +0 -347
|
@@ -1,347 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Configuration Persistence Manager
|
|
3
|
-
* Saves deployment configurations to customer config files for reuse
|
|
4
|
-
*
|
|
5
|
-
* This eliminates developer friction by:
|
|
6
|
-
* - Saving all deployment inputs after successful deployment
|
|
7
|
-
* - Auto-loading configurations for repeat deployments
|
|
8
|
-
* - Eliminating re-entry of account IDs, zone IDs, URLs, etc.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync } from 'fs';
|
|
12
|
-
import { join, dirname } from 'path';
|
|
13
|
-
import { fileURLToPath } from 'url';
|
|
14
|
-
|
|
15
|
-
// ESM-compatible __dirname and __filename
|
|
16
|
-
let __dirname;
|
|
17
|
-
let __filename;
|
|
18
|
-
try {
|
|
19
|
-
__filename = fileURLToPath(import.meta.url);
|
|
20
|
-
__dirname = dirname(__filename);
|
|
21
|
-
} catch {
|
|
22
|
-
// Fallback for test environments
|
|
23
|
-
__dirname = process.cwd();
|
|
24
|
-
__filename = __filename || 'config-persistence.js';
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* ConfigPersistenceManager
|
|
29
|
-
* Handles saving and loading deployment configurations
|
|
30
|
-
*/
|
|
31
|
-
export class ConfigPersistenceManager {
|
|
32
|
-
constructor(options = {}) {
|
|
33
|
-
// Default to config/customers in the target service directory
|
|
34
|
-
this.configDir = options.configDir || join(process.cwd(), 'config', 'customers');
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Save deployment configuration to customer environment file
|
|
39
|
-
* @param {Object} deployment - Complete deployment configuration
|
|
40
|
-
* @param {string} deployment.customer - Customer name
|
|
41
|
-
* @param {string} deployment.environment - Environment (development/staging/production)
|
|
42
|
-
* @param {Object} deployment.coreInputs - Tier 1 core inputs
|
|
43
|
-
* @param {Object} deployment.confirmations - Tier 2 confirmations
|
|
44
|
-
* @param {Object} deployment.result - Deployment result
|
|
45
|
-
*/
|
|
46
|
-
async saveDeploymentConfig(deployment) {
|
|
47
|
-
const {
|
|
48
|
-
customer,
|
|
49
|
-
environment,
|
|
50
|
-
coreInputs,
|
|
51
|
-
confirmations,
|
|
52
|
-
result
|
|
53
|
-
} = deployment;
|
|
54
|
-
if (!customer || !environment) {
|
|
55
|
-
throw new Error('Customer and environment are required');
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Create customer directory if it doesn't exist
|
|
59
|
-
const customerDir = join(this.configDir, customer);
|
|
60
|
-
if (!existsSync(customerDir)) {
|
|
61
|
-
mkdirSync(customerDir, {
|
|
62
|
-
recursive: true
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Generate environment file content
|
|
67
|
-
const envContent = this.generateEnvFileContent({
|
|
68
|
-
customer,
|
|
69
|
-
environment,
|
|
70
|
-
coreInputs,
|
|
71
|
-
confirmations,
|
|
72
|
-
result
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
// Write to customer environment file
|
|
76
|
-
const envFile = join(customerDir, `${environment}.env`);
|
|
77
|
-
writeFileSync(envFile, envContent, 'utf8');
|
|
78
|
-
console.log(` 💾 Configuration saved: ${envFile}`);
|
|
79
|
-
return envFile;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Generate .env file content from deployment data
|
|
84
|
-
* @param {Object} data - Deployment data
|
|
85
|
-
* @returns {string} .env file content
|
|
86
|
-
*/
|
|
87
|
-
generateEnvFileContent(data) {
|
|
88
|
-
const {
|
|
89
|
-
customer,
|
|
90
|
-
environment,
|
|
91
|
-
coreInputs,
|
|
92
|
-
confirmations,
|
|
93
|
-
result
|
|
94
|
-
} = data;
|
|
95
|
-
const timestamp = new Date().toISOString();
|
|
96
|
-
const lines = [`# Deployment Configuration - ${customer} (${environment})`, `# Last Updated: ${timestamp}`, `# Auto-generated by Clodo Framework deployment`, '', '# ============================================', '# Core Customer Identity', '# ============================================', `CUSTOMER_ID=${customer}`, `CUSTOMER_NAME=${customer}`, `ENVIRONMENT=${environment}`, ''];
|
|
97
|
-
|
|
98
|
-
// Cloudflare Configuration
|
|
99
|
-
if (coreInputs.cloudflareAccountId || coreInputs.cloudflareZoneId) {
|
|
100
|
-
lines.push('# ============================================');
|
|
101
|
-
lines.push('# Cloudflare Configuration');
|
|
102
|
-
lines.push('# ============================================');
|
|
103
|
-
if (coreInputs.cloudflareAccountId) {
|
|
104
|
-
lines.push(`CLOUDFLARE_ACCOUNT_ID=${coreInputs.cloudflareAccountId}`);
|
|
105
|
-
}
|
|
106
|
-
if (coreInputs.cloudflareZoneId) {
|
|
107
|
-
lines.push(`CLOUDFLARE_ZONE_ID=${coreInputs.cloudflareZoneId}`);
|
|
108
|
-
}
|
|
109
|
-
if (coreInputs.domainName) {
|
|
110
|
-
lines.push(`CUSTOMER_DOMAIN=${coreInputs.domainName}`);
|
|
111
|
-
lines.push(`DOMAIN=${coreInputs.domainName}`);
|
|
112
|
-
}
|
|
113
|
-
lines.push('# CLOUDFLARE_API_TOKEN=<stored-securely-in-env>');
|
|
114
|
-
lines.push('');
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Service Configuration
|
|
118
|
-
lines.push('# ============================================');
|
|
119
|
-
lines.push('# Service Configuration');
|
|
120
|
-
lines.push('# ============================================');
|
|
121
|
-
if (coreInputs.serviceName) {
|
|
122
|
-
lines.push(`SERVICE_NAME=${coreInputs.serviceName}`);
|
|
123
|
-
}
|
|
124
|
-
if (coreInputs.serviceType) {
|
|
125
|
-
lines.push(`SERVICE_TYPE=${coreInputs.serviceType}`);
|
|
126
|
-
}
|
|
127
|
-
if (confirmations.displayName) {
|
|
128
|
-
lines.push(`DISPLAY_NAME=${confirmations.displayName}`);
|
|
129
|
-
}
|
|
130
|
-
if (confirmations.description) {
|
|
131
|
-
lines.push(`DESCRIPTION=${confirmations.description}`);
|
|
132
|
-
}
|
|
133
|
-
if (confirmations.version) {
|
|
134
|
-
lines.push(`VERSION=${confirmations.version}`);
|
|
135
|
-
}
|
|
136
|
-
if (confirmations.author) {
|
|
137
|
-
lines.push(`AUTHOR=${confirmations.author}`);
|
|
138
|
-
}
|
|
139
|
-
lines.push('');
|
|
140
|
-
|
|
141
|
-
// Database & Worker
|
|
142
|
-
if (confirmations.databaseName || confirmations.workerName) {
|
|
143
|
-
lines.push('# ============================================');
|
|
144
|
-
lines.push('# Cloudflare Resources');
|
|
145
|
-
lines.push('# ============================================');
|
|
146
|
-
if (confirmations.workerName) {
|
|
147
|
-
lines.push(`WORKER_NAME=${confirmations.workerName}`);
|
|
148
|
-
}
|
|
149
|
-
if (confirmations.databaseName) {
|
|
150
|
-
lines.push(`DATABASE_NAME=${confirmations.databaseName}`);
|
|
151
|
-
lines.push(`D1_DATABASE_NAME=${confirmations.databaseName}`);
|
|
152
|
-
}
|
|
153
|
-
lines.push('');
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// URLs & Endpoints
|
|
157
|
-
if (confirmations.productionUrl || confirmations.stagingUrl || confirmations.developmentUrl) {
|
|
158
|
-
lines.push('# ============================================');
|
|
159
|
-
lines.push('# Environment URLs');
|
|
160
|
-
lines.push('# ============================================');
|
|
161
|
-
if (confirmations.productionUrl) {
|
|
162
|
-
lines.push(`PRODUCTION_URL=${confirmations.productionUrl}`);
|
|
163
|
-
}
|
|
164
|
-
if (confirmations.stagingUrl) {
|
|
165
|
-
lines.push(`STAGING_URL=${confirmations.stagingUrl}`);
|
|
166
|
-
}
|
|
167
|
-
if (confirmations.developmentUrl) {
|
|
168
|
-
lines.push(`DEVELOPMENT_URL=${confirmations.developmentUrl}`);
|
|
169
|
-
}
|
|
170
|
-
if (confirmations.documentationUrl) {
|
|
171
|
-
lines.push(`DOCUMENTATION_URL=${confirmations.documentationUrl}`);
|
|
172
|
-
}
|
|
173
|
-
if (confirmations.deploymentUrl) {
|
|
174
|
-
lines.push(`DEPLOYMENT_URL=${confirmations.deploymentUrl}`);
|
|
175
|
-
}
|
|
176
|
-
if (result && result.url) {
|
|
177
|
-
lines.push(`API_URL=${result.url}`);
|
|
178
|
-
}
|
|
179
|
-
lines.push('');
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// API Configuration
|
|
183
|
-
if (confirmations.packageName || confirmations.apiBasePath || confirmations.healthCheckPath) {
|
|
184
|
-
lines.push('# ============================================');
|
|
185
|
-
lines.push('# API Configuration');
|
|
186
|
-
lines.push('# ============================================');
|
|
187
|
-
if (confirmations.packageName) {
|
|
188
|
-
lines.push(`PACKAGE_NAME=${confirmations.packageName}`);
|
|
189
|
-
}
|
|
190
|
-
if (confirmations.apiBasePath) {
|
|
191
|
-
lines.push(`API_BASE_PATH=${confirmations.apiBasePath}`);
|
|
192
|
-
}
|
|
193
|
-
if (confirmations.healthCheckPath) {
|
|
194
|
-
lines.push(`HEALTH_CHECK_PATH=${confirmations.healthCheckPath}`);
|
|
195
|
-
}
|
|
196
|
-
lines.push(`API_VERSION=v1`);
|
|
197
|
-
lines.push('');
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// Feature Flags
|
|
201
|
-
if (confirmations.features && Object.keys(confirmations.features).length > 0) {
|
|
202
|
-
lines.push('# ============================================');
|
|
203
|
-
lines.push('# Feature Flags');
|
|
204
|
-
lines.push('# ============================================');
|
|
205
|
-
Object.entries(confirmations.features).forEach(([feature, enabled]) => {
|
|
206
|
-
const featureName = feature.toUpperCase().replace(/([A-Z])/g, '_$1').replace(/^_/, '');
|
|
207
|
-
lines.push(`FEATURE_${featureName}=${enabled ? 'true' : 'false'}`);
|
|
208
|
-
});
|
|
209
|
-
lines.push('');
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// Environment-specific settings
|
|
213
|
-
lines.push('# ============================================');
|
|
214
|
-
lines.push(`# ${environment.charAt(0).toUpperCase() + environment.slice(1)} Environment Settings`);
|
|
215
|
-
lines.push('# ============================================');
|
|
216
|
-
switch (environment) {
|
|
217
|
-
case 'development':
|
|
218
|
-
lines.push('ENABLE_DEBUG_MODE=true');
|
|
219
|
-
lines.push('ENABLE_DETAILED_LOGGING=true');
|
|
220
|
-
lines.push('LOG_LEVEL=debug');
|
|
221
|
-
lines.push('RATE_LIMIT_REQUESTS_PER_MINUTE=10000');
|
|
222
|
-
lines.push('CORS_ORIGINS=*');
|
|
223
|
-
break;
|
|
224
|
-
case 'staging':
|
|
225
|
-
lines.push('ENABLE_DEBUG_MODE=true');
|
|
226
|
-
lines.push('LOG_LEVEL=info');
|
|
227
|
-
lines.push('RATE_LIMIT_REQUESTS_PER_MINUTE=1000');
|
|
228
|
-
break;
|
|
229
|
-
case 'production':
|
|
230
|
-
lines.push('ENABLE_DEBUG_MODE=false');
|
|
231
|
-
lines.push('LOG_LEVEL=warn');
|
|
232
|
-
lines.push('RATE_LIMIT_REQUESTS_PER_MINUTE=60');
|
|
233
|
-
lines.push('# Add production-specific secrets via Cloudflare dashboard or wrangler');
|
|
234
|
-
break;
|
|
235
|
-
}
|
|
236
|
-
lines.push('');
|
|
237
|
-
|
|
238
|
-
// Monitoring
|
|
239
|
-
lines.push('# ============================================');
|
|
240
|
-
lines.push('# Monitoring & Observability');
|
|
241
|
-
lines.push('# ============================================');
|
|
242
|
-
lines.push('METRICS_ENABLED=true');
|
|
243
|
-
lines.push('TRACING_ENABLED=true');
|
|
244
|
-
lines.push('ERROR_REPORTING_ENABLED=true');
|
|
245
|
-
lines.push('');
|
|
246
|
-
|
|
247
|
-
// Deployment metadata
|
|
248
|
-
lines.push('# ============================================');
|
|
249
|
-
lines.push('# Deployment Metadata');
|
|
250
|
-
lines.push('# ============================================');
|
|
251
|
-
lines.push(`DEPLOYED_AT=${timestamp}`);
|
|
252
|
-
if (result && result.status) {
|
|
253
|
-
lines.push(`DEPLOYMENT_STATUS=${result.status}`);
|
|
254
|
-
}
|
|
255
|
-
lines.push('');
|
|
256
|
-
return lines.join('\n');
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Check if customer configuration exists
|
|
261
|
-
* @param {string} customer - Customer name
|
|
262
|
-
* @param {string} environment - Environment
|
|
263
|
-
* @returns {boolean} True if configuration exists
|
|
264
|
-
*/
|
|
265
|
-
configExists(customer, environment) {
|
|
266
|
-
const envFile = join(this.configDir, customer, `${environment}.env`);
|
|
267
|
-
return existsSync(envFile);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Get all configured customers
|
|
272
|
-
* @returns {Array<string>} List of customer names
|
|
273
|
-
*/
|
|
274
|
-
getConfiguredCustomers() {
|
|
275
|
-
if (!existsSync(this.configDir)) {
|
|
276
|
-
return [];
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// ESM-compatible fs import (already imported at top)
|
|
280
|
-
const entries = readdirSync(this.configDir, {
|
|
281
|
-
withFileTypes: true
|
|
282
|
-
});
|
|
283
|
-
return entries.filter(entry => entry.isDirectory() && entry.name !== 'template').map(entry => entry.name);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Load environment file and parse into object
|
|
288
|
-
* @param {string} customer - Customer name
|
|
289
|
-
* @param {string} environment - Environment
|
|
290
|
-
* @returns {Object|null} Parsed environment variables or null if not found
|
|
291
|
-
*/
|
|
292
|
-
loadEnvironmentConfig(customer, environment) {
|
|
293
|
-
const envFile = join(this.configDir, customer, `${environment}.env`);
|
|
294
|
-
if (!existsSync(envFile)) {
|
|
295
|
-
return null;
|
|
296
|
-
}
|
|
297
|
-
const content = readFileSync(envFile, 'utf8');
|
|
298
|
-
const envVars = {};
|
|
299
|
-
content.split('\n').forEach(line => {
|
|
300
|
-
line = line.trim();
|
|
301
|
-
|
|
302
|
-
// Skip comments and empty lines
|
|
303
|
-
if (!line || line.startsWith('#')) {
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Parse KEY=value
|
|
308
|
-
const match = line.match(/^([A-Z_][A-Z0-9_]*)=(.*)$/);
|
|
309
|
-
if (match) {
|
|
310
|
-
const [, key, value] = match;
|
|
311
|
-
envVars[key] = value;
|
|
312
|
-
}
|
|
313
|
-
});
|
|
314
|
-
return envVars;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* Display customer configuration summary
|
|
319
|
-
* @param {string} customer - Customer name
|
|
320
|
-
* @param {string} environment - Environment
|
|
321
|
-
*/
|
|
322
|
-
displayCustomerConfig(customer, environment) {
|
|
323
|
-
const config = this.loadEnvironmentConfig(customer, environment);
|
|
324
|
-
if (!config) {
|
|
325
|
-
console.log(` ⚠️ No configuration found for ${customer}/${environment}`);
|
|
326
|
-
return;
|
|
327
|
-
}
|
|
328
|
-
console.log(`\n 📋 Existing Configuration for ${customer}/${environment}:`);
|
|
329
|
-
console.log(' ' + '─'.repeat(60));
|
|
330
|
-
const highlights = ['CUSTOMER_DOMAIN', 'CLOUDFLARE_ACCOUNT_ID', 'CLOUDFLARE_ZONE_ID', 'SERVICE_NAME', 'WORKER_NAME', 'DATABASE_NAME', 'DEPLOYMENT_URL', 'DEPLOYED_AT'];
|
|
331
|
-
highlights.forEach(key => {
|
|
332
|
-
if (config[key]) {
|
|
333
|
-
let value = config[key];
|
|
334
|
-
|
|
335
|
-
// Truncate long IDs for display
|
|
336
|
-
if (key.includes('_ID') && value.length > 20) {
|
|
337
|
-
value = value.substring(0, 8) + '...' + value.substring(value.length - 4);
|
|
338
|
-
}
|
|
339
|
-
console.log(` ${key.padEnd(25)}: ${value}`);
|
|
340
|
-
}
|
|
341
|
-
});
|
|
342
|
-
console.log(' ' + '─'.repeat(60));
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
// Export singleton instance for convenience
|
|
347
|
-
export const configPersistence = new ConfigPersistenceManager();
|