@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.
@@ -1,208 +0,0 @@
1
- import { ConfigurationValidator } from './ConfigurationValidator.js';
2
- import { SecretGenerator } from './SecretGenerator.js';
3
- import { checkHealth } from '../utils/health-checker.js';
4
-
5
- /**
6
- * Secure Deployment Manager
7
- * Manages secure deployment pipeline with security validation
8
- */
9
- export class DeploymentManager {
10
- /**
11
- * Deploy customer configuration with security validation
12
- * @param {Object} options - Deployment options
13
- */
14
- static async deployWithSecurity(options) {
15
- const {
16
- customer,
17
- environment,
18
- skipValidation = false,
19
- allowInsecure = false,
20
- dryRun = false,
21
- deploymentUrl // New parameter for real URL extraction
22
- } = options;
23
- console.log(`\n๐Ÿš€ Secure Deployment: ${customer}/${environment}`);
24
- console.log('='.repeat(60));
25
- try {
26
- // Step 1: Validate configuration
27
- if (!skipValidation) {
28
- console.log('\n๐Ÿ” Step 1: Configuration Validation');
29
- await this.validateConfiguration(customer, environment, {
30
- allowInsecure
31
- });
32
- }
33
-
34
- // Step 2: Security validation
35
- console.log('\n๐Ÿ”’ Step 2: Security Validation');
36
- const securityResult = ConfigurationValidator.validateConfiguration(customer, environment);
37
- if (!securityResult.valid) {
38
- const criticalIssues = securityResult.securityIssues.filter(issue => issue.severity === 'critical');
39
- if (criticalIssues.length > 0 && !allowInsecure) {
40
- console.log('\nโŒ DEPLOYMENT BLOCKED');
41
- console.log('Critical security issues must be resolved before deployment.');
42
- console.log('\nTo resolve:');
43
- console.log('1. Fix the security issues listed above');
44
- console.log('2. Use secure secrets management');
45
- console.log('3. Generate secure keys with SecretGenerator');
46
- if (dryRun) {
47
- console.log('\n๐Ÿงช DRY RUN: Would have been blocked by security issues');
48
- } else {
49
- throw new Error('Deployment blocked due to critical security issues');
50
- }
51
- }
52
- }
53
-
54
- // Step 3: Pre-deployment checks
55
- console.log('\nโœ… Step 3: Pre-deployment Checks');
56
- await this.performPreDeploymentChecks(customer, environment);
57
-
58
- // Step 4: Deploy (or dry run)
59
- if (dryRun) {
60
- console.log('\n๐Ÿงช DRY RUN: Would deploy with security validation');
61
- console.log(` Customer: ${customer}`);
62
- console.log(` Environment: ${environment}`);
63
- console.log(` Security validation: ${securityResult.valid ? 'PASSED' : 'ISSUES FOUND'}`);
64
- console.log('\nโœ… Dry run completed successfully');
65
- } else {
66
- console.log('\n๐Ÿš€ Step 4: Deploying');
67
- const deployResult = await this.performDeployment(customer, environment);
68
- console.log(` โœ… Deployment URL: ${deployResult.url}`);
69
- }
70
-
71
- // Step 5: Post-deployment validation (real implementation)
72
- if (!dryRun && deploymentUrl) {
73
- console.log('\n๐Ÿ” Step 5: Post-deployment Validation');
74
- await this.performPostDeploymentChecks(customer, environment, deploymentUrl);
75
- }
76
- console.log('\n๐ŸŽ‰ Deployment completed successfully!');
77
- } catch (error) {
78
- console.error(`\nโŒ Deployment failed: ${error.message}`);
79
- throw error;
80
- }
81
- }
82
-
83
- /**
84
- * Validate configuration before deployment
85
- */
86
- static async validateConfiguration(customer, environment, options = {}) {
87
- const {
88
- allowInsecure = false
89
- } = options;
90
- try {
91
- // This would integrate with the framework's config builder
92
- // For now, we'll assume configuration validation is handled elsewhere
93
- console.log(` โœ“ Configuration structure validated for ${customer}/${environment}`);
94
- } catch (error) {
95
- if (!allowInsecure) {
96
- throw new Error(`Configuration validation failed: ${error.message}`);
97
- }
98
- console.log(` โš ๏ธ Configuration validation failed but proceeding (--allow-insecure)`);
99
- }
100
- }
101
-
102
- /**
103
- * Perform pre-deployment checks
104
- */
105
- // eslint-disable-next-line no-unused-vars
106
- static async performPreDeploymentChecks(_customer, _environment) {
107
- const checks = ['Environment variables validation', 'Required secrets presence', 'Configuration consistency', 'Resource availability'];
108
- for (const check of checks) {
109
- console.log(` โœ“ ${check}`);
110
- // Add actual check logic here
111
- }
112
- }
113
-
114
- /**
115
- * Perform the actual deployment
116
- */
117
- static async performDeployment(_customer, _environment) {
118
- // This would integrate with the actual deployment system
119
- // For now, simulate deployment
120
- console.log(` ๐Ÿš€ Deploying ${_customer} to ${_environment} environment`);
121
-
122
- // Simulate deployment steps
123
- await this.delay(1000);
124
- console.log(` โœ“ Deployment package prepared`);
125
- await this.delay(1000);
126
- console.log(` โœ“ Deploying to Cloudflare Workers`);
127
- await this.delay(2000);
128
- console.log(` โœ“ Deployment completed`);
129
-
130
- // Return deployment result with URL
131
- const deploymentUrl = `https://${_customer}-${_environment}.workers.dev`;
132
- return {
133
- url: deploymentUrl,
134
- customer: _customer,
135
- environment: _environment,
136
- timestamp: new Date().toISOString()
137
- };
138
- }
139
-
140
- /**
141
- * Perform post-deployment checks (real HTTP-based validation)
142
- */
143
- static async performPostDeploymentChecks(customer, environment, deploymentUrl) {
144
- console.log(` ๐Ÿ” Validating deployment at ${deploymentUrl}`);
145
- try {
146
- // Real health check using HTTP
147
- const healthResult = await checkHealth(deploymentUrl);
148
- if (healthResult.status !== 'ok') {
149
- throw new Error(`Health check failed: ${healthResult.message}`);
150
- }
151
- console.log(` โœ… Health check passed for ${customer}/${environment}`);
152
- } catch (error) {
153
- console.error(` โŒ Post-deployment validation failed: ${error.message}`);
154
- throw error;
155
- }
156
- }
157
-
158
- /**
159
- * Generate secure deployment configuration
160
- */
161
- static generateSecureConfig(customer, environment) {
162
- const config = {
163
- customer,
164
- environment,
165
- secrets: {},
166
- security: {
167
- validated: true,
168
- timestamp: new Date().toISOString()
169
- }
170
- };
171
-
172
- // Generate required secure keys
173
- const requiredKeys = ['CONTENT_SKIMMER_API_KEY', 'LOGGER_SERVICE_API_KEY', 'AUTH_SERVICE_API_KEY', 'X_SERVICE_KEY'];
174
- for (const keyName of requiredKeys) {
175
- config.secrets[keyName] = SecretGenerator.generateServiceKey(keyName.replace('_API_KEY', '').toLowerCase().replace('_', '-'), environment);
176
- }
177
-
178
- // Generate JWT secret
179
- config.secrets['AUTH_JWT_SECRET'] = SecretGenerator.generateSecureJwtSecret();
180
- return config;
181
- }
182
-
183
- /**
184
- * Validate deployment readiness
185
- */
186
- // eslint-disable-next-line no-unused-vars
187
- static validateDeploymentReadiness(_customer, _environment) {
188
- const issues = [];
189
-
190
- // Check if all required secrets are available
191
- // eslint-disable-next-line no-unused-vars
192
- const requiredSecrets = ['CONTENT_SKIMMER_API_KEY', 'LOGGER_SERVICE_API_KEY', 'AUTH_SERVICE_API_KEY', 'X_SERVICE_KEY', 'AUTH_JWT_SECRET'];
193
-
194
- // This would check actual secret availability
195
- // For now, return empty issues array
196
- return {
197
- ready: issues.length === 0,
198
- issues
199
- };
200
- }
201
-
202
- /**
203
- * Utility method for delays in async operations
204
- */
205
- static delay(ms) {
206
- return new Promise(resolve => setTimeout(resolve, ms));
207
- }
208
- }
@@ -1,130 +0,0 @@
1
- /**
2
- * Configuration Mutator Module
3
- * Focused module for safe configuration file mutations
4
- */
5
-
6
- import fs from 'fs/promises';
7
- import path from 'path';
8
- export class ConfigMutator {
9
- constructor(options = {}) {
10
- this.dryRun = options.dryRun || false;
11
- }
12
-
13
- /**
14
- * Safely update domain configuration
15
- */
16
- async updateDomainConfig(servicePath, currentConfig, updates) {
17
- const domainConfigPath = path.join(servicePath, 'src/config/domains.js');
18
- let domainConfig = await fs.readFile(domainConfigPath, 'utf8');
19
- if (updates.domainName) {
20
- // Use regex-based replacement for safety
21
- domainConfig = domainConfig.replace(new RegExp(`name: ['"]${this.escapeRegExp(currentConfig.domainName)}['"]`, 'g'), `name: '${updates.domainName}'`);
22
-
23
- // Update domain URLs
24
- domainConfig = domainConfig.replace(new RegExp(`https://[^'"]*${this.escapeRegExp(currentConfig.domainName)}[^'"]*`, 'g'), match => match.replace(currentConfig.domainName, updates.domainName));
25
- }
26
- if (!this.dryRun) {
27
- await fs.writeFile(domainConfigPath, domainConfig, 'utf8');
28
- }
29
- return {
30
- updated: true,
31
- path: domainConfigPath
32
- };
33
- }
34
-
35
- /**
36
- * Update Cloudflare configuration
37
- */
38
- async updateCloudflareConfig(servicePath, currentConfig, updates) {
39
- const domainConfigPath = path.join(servicePath, 'src/config/domains.js');
40
- let domainConfig = await fs.readFile(domainConfigPath, 'utf8');
41
- if (updates.accountId) {
42
- domainConfig = domainConfig.replace(new RegExp(`accountId:\\s*['"]${this.escapeRegExp(currentConfig.cloudflareAccountId || '')}['"]`, 'g'), `accountId: '${updates.accountId}'`);
43
- }
44
- if (updates.zoneId) {
45
- domainConfig = domainConfig.replace(new RegExp(`zoneId:\\s*['"]${this.escapeRegExp(currentConfig.cloudflareZoneId || '')}['"]`, 'g'), `zoneId: '${updates.zoneId}'`);
46
- }
47
- if (!this.dryRun) {
48
- await fs.writeFile(domainConfigPath, domainConfig, 'utf8');
49
- }
50
- return {
51
- updated: true,
52
- path: domainConfigPath
53
- };
54
- }
55
-
56
- /**
57
- * Update environment configuration
58
- */
59
- async updateEnvironmentConfig(servicePath, currentConfig, updates) {
60
- const wranglerConfigPath = path.join(servicePath, 'wrangler.toml');
61
- let wranglerConfig = await fs.readFile(wranglerConfigPath, 'utf8');
62
- if (updates.environment) {
63
- // Update environment-specific names
64
- wranglerConfig = wranglerConfig.replace(new RegExp(`name\\s*=\\s*['"].*${this.escapeRegExp(currentConfig.environment)}.*['"]`, 'g'), `name = "${currentConfig.serviceName}-${updates.environment}"`);
65
- wranglerConfig = wranglerConfig.replace(new RegExp(`\\[env\\.${this.escapeRegExp(currentConfig.environment)}\\]`, 'g'), `[env.${updates.environment}]`);
66
- }
67
- if (!this.dryRun) {
68
- await fs.writeFile(wranglerConfigPath, wranglerConfig, 'utf8');
69
- }
70
- return {
71
- updated: true,
72
- path: wranglerConfigPath
73
- };
74
- }
75
-
76
- /**
77
- * Update feature configuration
78
- */
79
- async updateFeatureConfig(servicePath, action, feature) {
80
- const domainConfigPath = path.join(servicePath, 'src/config/domains.js');
81
- let domainConfig = await fs.readFile(domainConfigPath, 'utf8');
82
- if (action === 'add') {
83
- // Add feature flag
84
- const featureLine = ` ${feature}: true,`;
85
- domainConfig = domainConfig.replace(/(\s+)(cors:\s*(?:true|false),?)(\n)/, `$1$2$3$1${featureLine}$3`);
86
- } else if (action === 'remove') {
87
- // Remove feature flag
88
- const featureLinePattern = new RegExp(`\\s+${this.escapeRegExp(feature)}:\\s*(?:true|false),?\\n`, 'g');
89
- domainConfig = domainConfig.replace(featureLinePattern, '');
90
- }
91
- if (!this.dryRun) {
92
- await fs.writeFile(domainConfigPath, domainConfig, 'utf8');
93
- }
94
- return {
95
- updated: true,
96
- path: domainConfigPath
97
- };
98
- }
99
-
100
- /**
101
- * Validate configuration file exists and is writable
102
- */
103
- async validateConfigFile(filePath) {
104
- try {
105
- await fs.access(filePath, fs.constants.R_OK | fs.constants.W_OK);
106
- return {
107
- valid: true
108
- };
109
- } catch (error) {
110
- return {
111
- valid: false,
112
- error: `Configuration file not accessible: ${filePath}`
113
- };
114
- }
115
- }
116
-
117
- /**
118
- * Escape special regex characters for safe replacement
119
- */
120
- escapeRegExp(string) {
121
- return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
122
- }
123
-
124
- /**
125
- * Enable/disable dry run mode
126
- */
127
- setDryRun(enabled) {
128
- this.dryRun = enabled;
129
- }
130
- }
@@ -1,175 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Customer Configuration Management CLI
5
- * Manages multi-environment, multi-customer configuration structure
6
- * Integrates with Clodo Framework domain and feature flag systems
7
- */
8
- import { CustomerConfigCLI } from '../../../dist/config/CustomerConfigCLI.js';
9
- import { resolve } from 'path';
10
-
11
- // Parse command line arguments
12
- const argv = process.argv.slice(2);
13
- let configDir = null;
14
- let command = null;
15
- let args = [];
16
-
17
- // Extract --config-dir parameter if present
18
- for (let i = 0; i < argv.length; i++) {
19
- if (argv[i] === '--config-dir' && i + 1 < argv.length) {
20
- configDir = resolve(argv[i + 1]);
21
- i++; // Skip the next argument (the path)
22
- } else if (!command) {
23
- command = argv[i];
24
- } else {
25
- args.push(argv[i]);
26
- }
27
- }
28
-
29
- // Default to current working directory if not specified
30
- if (!configDir) {
31
- configDir = resolve(process.cwd(), 'config');
32
- }
33
- async function main() {
34
- const cli = new CustomerConfigCLI({
35
- configDir
36
- });
37
- await cli.initialize();
38
- try {
39
- switch (command) {
40
- case 'create-customer':
41
- const [customerName, domain] = args;
42
- const result = await cli.createCustomer(customerName, domain);
43
- if (result.success) {
44
- console.log(`\n๐ŸŽ‰ Customer ${customerName} configuration created successfully!`);
45
- console.log(`\n๐Ÿ“‹ Customer Details:`);
46
- console.log(` Name: ${result.customer.name}`);
47
- console.log(` Domain: ${result.customer.domain || 'Not specified'}`);
48
- console.log(` Config Path: ${result.customer.configPath}`);
49
- console.log(` Environments: ${result.customer.environments.join(', ')}`);
50
- console.log(`\n๐Ÿ“‹ Next steps:`);
51
- console.log(`1. Review generated configs in: config/customers/${customerName}/`);
52
- console.log(`2. Update domain-specific URLs if needed`);
53
- console.log(`3. Generate production secrets: npm run security:generate-key ${customerName}`);
54
- console.log(`4. Set production secrets: wrangler secret put KEY_NAME --env production`);
55
- } else {
56
- console.error(`โŒ Failed to create customer: ${result.error}`);
57
- process.exit(1);
58
- }
59
- break;
60
- case 'validate':
61
- const validateResult = await cli.validateConfigurations();
62
- if (validateResult.valid) {
63
- console.log('โœ… All customer configurations are valid');
64
- } else {
65
- console.log('โŒ Configuration validation failed');
66
- validateResult.errors.forEach(error => console.log(` - ${error}`));
67
- process.exit(1);
68
- }
69
- break;
70
- case 'show':
71
- const [customerNameShow, environment] = args;
72
- const showResult = cli.showConfiguration(customerNameShow, environment);
73
- if (showResult.success) {
74
- console.log(`๐Ÿ” Effective configuration: ${customerNameShow}/${environment}\n`);
75
- if (showResult.config.variables?.base) {
76
- console.log('๐Ÿ“‹ Base variables:');
77
- Object.entries(showResult.config.variables.base).slice(0, 10).forEach(([key, value]) => {
78
- console.log(` ${key}=${value}`);
79
- });
80
- if (Object.keys(showResult.config.variables.base).length > 10) {
81
- console.log(' ...');
82
- }
83
- console.log('');
84
- }
85
- if (showResult.config.variables?.customer) {
86
- console.log(`๐Ÿ“‹ Customer ${environment} variables:`);
87
- Object.entries(showResult.config.variables.customer).slice(0, 15).forEach(([key, value]) => {
88
- console.log(` ${key}=${value}`);
89
- });
90
- if (Object.keys(showResult.config.variables.customer).length > 15) {
91
- console.log(' ...');
92
- }
93
- console.log('');
94
- }
95
- if (showResult.config.features && Object.keys(showResult.config.features).length > 0) {
96
- console.log('๐Ÿšฉ Customer features:');
97
- Object.entries(showResult.config.features).forEach(([feature, enabled]) => {
98
- console.log(` ${feature}: ${enabled ? 'โœ…' : 'โŒ'}`);
99
- });
100
- }
101
- } else {
102
- console.error(`โŒ Failed to show configuration: ${showResult.error}`);
103
- process.exit(1);
104
- }
105
- break;
106
- case 'deploy-command':
107
- const [customerNameDeploy, environmentDeploy] = args;
108
- const deployResult = cli.getDeployCommand(customerNameDeploy, environmentDeploy);
109
- if (deployResult.success) {
110
- console.log(`๐Ÿ“‹ Deploy command for ${customerNameDeploy}/${environmentDeploy}:`);
111
- console.log(` ${deployResult.command}`);
112
- console.log(`\n๐Ÿ’ก Ensure customer config is loaded: ${deployResult.configPath}`);
113
- } else {
114
- console.error(`โŒ Failed to get deploy command: ${deployResult.error}`);
115
- process.exit(1);
116
- }
117
- break;
118
- case 'list':
119
- const listResult = cli.listCustomers();
120
- if (listResult.success && listResult.customers.length > 0) {
121
- console.log('๐Ÿ“‹ Configured customers:\n');
122
- listResult.customers.forEach(customer => {
123
- console.log(`๐Ÿข ${customer.name}`);
124
- console.log(` Domain: ${customer.customerDomain || customer.domain || 'Not configured'}`);
125
- console.log(` Account ID: ${customer.accountId ? `${customer.accountId.substring(0, 8)}...${customer.accountId.substring(24)}` : 'Not configured'}`);
126
- console.log(` Zone ID: ${customer.zoneId ? `${customer.zoneId.substring(0, 8)}...` : 'Not configured'}`);
127
- if (customer.databaseId) {
128
- console.log(` Database: ${customer.databaseName || 'Unnamed'} (${customer.databaseId.substring(0, 8)}...)`);
129
- }
130
- console.log(` Secrets: ${customer.hasSecrets ? 'โœ… Managed via wrangler secret commands' : 'โŒ Not configured'}`);
131
- console.log(` Environments: ${customer.environments.join(', ')}`);
132
- console.log(` Config Path: config/customers/${customer.name}/`);
133
- console.log('');
134
- });
135
- } else if (listResult.success) {
136
- console.log('๐Ÿ“‹ No customers configured');
137
- console.log('\n๐Ÿ’ก Tip: Run "clodo-customer-config create-customer <name>" to create your first customer');
138
- } else {
139
- console.error(`โŒ Failed to list customers: ${listResult.error}`);
140
- process.exit(1);
141
- }
142
- break;
143
- default:
144
- console.log('Customer Configuration Management Tool\n');
145
- console.log('Usage:');
146
- console.log(' clodo-customer-config [--config-dir <path>] <command> [args]\n');
147
- console.log('Options:');
148
- console.log(' --config-dir <path> - Path to config directory (default: ./config)\n');
149
- console.log('Available commands:');
150
- console.log(' create-customer <name> [domain] - Create new customer config from template');
151
- console.log(' validate - Validate configuration structure');
152
- console.log(' show <customer> <environment> - Show effective configuration');
153
- console.log(' deploy-command <customer> <env> - Get deployment command');
154
- console.log(' list - List all configured customers');
155
- console.log('\nExamples:');
156
- console.log(' clodo-customer-config create-customer acmecorp acmecorp.com');
157
- console.log(' clodo-customer-config validate');
158
- console.log(' clodo-customer-config show acmecorp production');
159
- console.log(' clodo-customer-config list');
160
- console.log(' clodo-customer-config --config-dir /path/to/service/config validate');
161
- console.log('\nIntegration:');
162
- console.log(' This tool integrates with Clodo Framework domain and feature flag systems.');
163
- console.log(' Customer configurations are automatically registered as domains.');
164
- console.log(' When run from a service directory, it uses ./config by default.');
165
- break;
166
- }
167
- } catch (error) {
168
- console.error(`โŒ Error: ${error.message}`);
169
- process.exit(1);
170
- }
171
- }
172
- main().catch(error => {
173
- console.error(`โŒ Unexpected error: ${error.message}`);
174
- process.exit(1);
175
- });