@tamyla/clodo-framework 3.1.21 → 3.1.22
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 +9 -0
- package/README.md +53 -0
- package/dist/bin/clodo-service.js +47 -15
- package/dist/bin/commands/deploy.js +115 -83
- package/dist/bin/commands/helpers/deployment-ui.js +138 -0
- package/dist/bin/commands/helpers/deployment-verification.js +251 -0
- package/dist/bin/commands/helpers/error-recovery.js +80 -0
- package/dist/bin/commands/helpers/resource-detection.js +113 -0
- package/dist/bin/commands/validate.js +1 -1
- package/dist/bin/security/security-cli.js +1 -1
- package/dist/bin/shared/cache/configuration-cache.js +82 -0
- package/dist/bin/shared/cloudflare/domain-manager.js +1 -1
- package/dist/bin/shared/cloudflare/index.js +1 -1
- package/dist/bin/shared/cloudflare/ops.js +6 -4
- package/dist/bin/shared/config/ConfigurationManager.js +23 -1
- package/dist/bin/shared/config/command-config-manager.js +19 -3
- package/dist/bin/shared/config/index.js +1 -1
- package/dist/bin/shared/deployment/credential-collector.js +30 -7
- package/dist/bin/shared/deployment/index.js +2 -2
- package/dist/bin/shared/deployment/rollback-manager.js +4 -520
- package/dist/bin/shared/deployment/utilities/d1-error-recovery.js +177 -0
- package/dist/bin/shared/deployment/validator.js +40 -10
- package/dist/bin/shared/deployment/workflows/deployment-summary.js +214 -0
- package/dist/bin/shared/deployment/workflows/interactive-confirmation.js +188 -0
- package/dist/bin/shared/deployment/workflows/interactive-database-workflow.js +234 -0
- package/dist/bin/shared/deployment/workflows/interactive-domain-info-gatherer.js +240 -0
- package/dist/bin/shared/deployment/workflows/interactive-secret-workflow.js +228 -0
- package/dist/bin/shared/deployment/workflows/interactive-testing-workflow.js +235 -0
- package/dist/bin/shared/deployment/workflows/interactive-validation.js +218 -0
- package/dist/bin/shared/error-handling/error-classifier.js +46 -0
- package/dist/bin/shared/monitoring/health-checker.js +129 -1
- package/dist/bin/shared/monitoring/memory-manager.js +17 -6
- package/dist/bin/shared/routing/domain-router.js +1 -1
- package/dist/bin/shared/utils/deployment-validator.js +97 -0
- package/dist/bin/shared/utils/formatters.js +10 -0
- package/dist/bin/shared/utils/index.js +13 -1
- package/dist/bin/shared/utils/interactive-prompts.js +34 -18
- package/dist/bin/shared/utils/progress-manager.js +2 -2
- package/dist/bin/shared/utils/progress-spinner.js +53 -0
- package/dist/bin/shared/utils/sensitive-redactor.js +91 -0
- package/dist/bin/shared/validation/ValidationRegistry.js +1 -1
- package/dist/security/index.js +1 -1
- package/dist/security/patterns/insecure-patterns.js +1 -1
- package/dist/utils/constants.js +102 -0
- package/dist/utils/deployment/wrangler-config-manager.js +215 -48
- package/dist/utils/framework-config.js +2 -2
- package/dist/utils/interactive-prompts.js +10 -59
- package/package.json +16 -8
- package/dist/bin/clodo-service-old.js +0 -868
- package/dist/bin/clodo-service-test.js +0 -10
- package/dist/bin/commands/assess.js +0 -91
- package/dist/bin/commands/create.js +0 -77
- package/dist/bin/commands/diagnose.js +0 -83
- package/dist/bin/commands/helpers.js +0 -138
- package/dist/bin/commands/update.js +0 -75
- package/dist/bin/database/deployment-db-manager.js +0 -423
- package/dist/bin/database/enterprise-db-manager.js +0 -457
- package/dist/bin/database/wrangler-d1-manager.js +0 -685
- package/dist/bin/deployment/enterprise-deploy.js +0 -877
- package/dist/bin/deployment/master-deploy.js +0 -1376
- package/dist/bin/deployment/modular-enterprise-deploy.js +0 -466
- package/dist/bin/deployment/modules/DeploymentConfiguration.js +0 -395
- package/dist/bin/deployment/modules/DeploymentOrchestrator.js +0 -492
- package/dist/bin/deployment/modules/EnvironmentManager.js +0 -517
- package/dist/bin/deployment/modules/MonitoringIntegration.js +0 -560
- package/dist/bin/deployment/modules/ValidationManager.js +0 -342
- package/dist/bin/deployment/orchestration/BaseDeploymentOrchestrator.js +0 -426
- package/dist/bin/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
- package/dist/bin/deployment/orchestration/PortfolioOrchestrator.js +0 -273
- package/dist/bin/deployment/orchestration/SingleServiceOrchestrator.js +0 -231
- package/dist/bin/deployment/orchestration/UnifiedDeploymentOrchestrator.js +0 -662
- package/dist/bin/deployment/test-interactive-utils.js +0 -66
- package/dist/bin/portfolio/portfolio-manager.js +0 -487
- package/dist/bin/service-management/create-service.js +0 -122
- package/dist/bin/service-management/init-service.js +0 -79
- package/dist/config/customers.js +0 -623
- package/dist/config/domains.js +0 -186
- package/dist/config/index.js +0 -6
- package/dist/database/database-orchestrator.js +0 -795
- package/dist/database/index.js +0 -4
- package/dist/deployment/index.js +0 -11
- package/dist/deployment/orchestration/BaseDeploymentOrchestrator.js +0 -426
- package/dist/deployment/orchestration/EnterpriseOrchestrator.js +0 -401
- package/dist/deployment/orchestration/PortfolioOrchestrator.js +0 -273
- package/dist/deployment/orchestration/SingleServiceOrchestrator.js +0 -231
- package/dist/deployment/orchestration/UnifiedDeploymentOrchestrator.js +0 -662
- package/dist/deployment/orchestration/index.js +0 -17
- package/dist/deployment/rollback-manager.js +0 -36
- package/dist/deployment/wrangler-deployer.js +0 -640
- package/dist/handlers/GenericRouteHandler.js +0 -532
- package/dist/migration/MigrationAdapters.js +0 -562
- package/dist/modules/ModuleManager.js +0 -668
- package/dist/modules/security.js +0 -96
- package/dist/orchestration/cross-domain-coordinator.js +0 -1083
- package/dist/orchestration/index.js +0 -5
- package/dist/orchestration/modules/DeploymentCoordinator.js +0 -368
- package/dist/orchestration/modules/DomainResolver.js +0 -198
- package/dist/orchestration/modules/StateManager.js +0 -332
- package/dist/orchestration/multi-domain-orchestrator.js +0 -724
- package/dist/routing/EnhancedRouter.js +0 -158
- package/dist/schema/SchemaManager.js +0 -778
- package/dist/service-management/ConfirmationEngine.js +0 -412
- package/dist/service-management/ErrorTracker.js +0 -299
- package/dist/service-management/GenerationEngine.js +0 -447
- package/dist/service-management/InputCollector.js +0 -619
- package/dist/service-management/ServiceCreator.js +0 -265
- package/dist/service-management/ServiceInitializer.js +0 -453
- package/dist/service-management/ServiceOrchestrator.js +0 -633
- package/dist/service-management/generators/BaseGenerator.js +0 -233
- package/dist/service-management/generators/GeneratorRegistry.js +0 -254
- package/dist/service-management/generators/cicd/CiWorkflowGenerator.js +0 -87
- package/dist/service-management/generators/cicd/DeployWorkflowGenerator.js +0 -106
- package/dist/service-management/generators/code/ServiceHandlersGenerator.js +0 -235
- package/dist/service-management/generators/code/ServiceMiddlewareGenerator.js +0 -116
- package/dist/service-management/generators/code/ServiceUtilsGenerator.js +0 -246
- package/dist/service-management/generators/code/WorkerIndexGenerator.js +0 -143
- package/dist/service-management/generators/config/DevelopmentEnvGenerator.js +0 -101
- package/dist/service-management/generators/config/DomainsConfigGenerator.js +0 -175
- package/dist/service-management/generators/config/EnvExampleGenerator.js +0 -178
- package/dist/service-management/generators/config/ProductionEnvGenerator.js +0 -97
- package/dist/service-management/generators/config/StagingEnvGenerator.js +0 -97
- package/dist/service-management/generators/config/WranglerTomlGenerator.js +0 -238
- package/dist/service-management/generators/core/PackageJsonGenerator.js +0 -243
- package/dist/service-management/generators/core/SiteConfigGenerator.js +0 -115
- package/dist/service-management/generators/documentation/ApiDocsGenerator.js +0 -331
- package/dist/service-management/generators/documentation/ConfigurationDocsGenerator.js +0 -294
- package/dist/service-management/generators/documentation/DeploymentDocsGenerator.js +0 -244
- package/dist/service-management/generators/documentation/ReadmeGenerator.js +0 -196
- package/dist/service-management/generators/schemas/ServiceSchemaGenerator.js +0 -190
- package/dist/service-management/generators/scripts/DeployScriptGenerator.js +0 -123
- package/dist/service-management/generators/scripts/HealthCheckScriptGenerator.js +0 -101
- package/dist/service-management/generators/scripts/SetupScriptGenerator.js +0 -88
- package/dist/service-management/generators/service-types/StaticSiteGenerator.js +0 -342
- package/dist/service-management/generators/testing/EslintConfigGenerator.js +0 -85
- package/dist/service-management/generators/testing/IntegrationTestsGenerator.js +0 -237
- package/dist/service-management/generators/testing/JestConfigGenerator.js +0 -72
- package/dist/service-management/generators/testing/UnitTestsGenerator.js +0 -277
- package/dist/service-management/generators/tooling/DockerComposeGenerator.js +0 -71
- package/dist/service-management/generators/tooling/GitignoreGenerator.js +0 -143
- package/dist/service-management/generators/utils/FileWriter.js +0 -179
- package/dist/service-management/generators/utils/PathResolver.js +0 -157
- package/dist/service-management/generators/utils/ServiceManifestGenerator.js +0 -111
- package/dist/service-management/generators/utils/TemplateEngine.js +0 -185
- package/dist/service-management/generators/utils/index.js +0 -18
- package/dist/service-management/handlers/ConfirmationHandler.js +0 -71
- package/dist/service-management/handlers/GenerationHandler.js +0 -80
- package/dist/service-management/handlers/InputHandler.js +0 -59
- package/dist/service-management/handlers/ValidationHandler.js +0 -203
- package/dist/service-management/index.js +0 -14
- package/dist/service-management/routing/DomainRouteMapper.js +0 -311
- package/dist/service-management/routing/RouteGenerator.js +0 -266
- package/dist/service-management/routing/WranglerRoutesBuilder.js +0 -273
- package/dist/service-management/routing/index.js +0 -14
- package/dist/service-management/services/DirectoryStructureService.js +0 -56
- package/dist/service-management/services/GenerationCoordinator.js +0 -208
- package/dist/service-management/services/GeneratorRegistry.js +0 -174
- package/dist/services/GenericDataService.js +0 -501
- package/dist/ui-structures/concepts/second-order-acquisition-strategy.md +0 -286
- package/dist/ui-structures/concepts/service-lifecycle-management.md +0 -150
- package/dist/ui-structures/concepts/service-manifest-guide.md +0 -309
- package/dist/ui-structures/concepts/three-tier-categorization-strategy.md +0 -231
- package/dist/ui-structures/creation/automated-generation-ui.json +0 -246
- package/dist/ui-structures/creation/core-inputs-ui.json +0 -217
- package/dist/ui-structures/creation/smart-confirmable-ui.json +0 -451
- package/dist/ui-structures/reference/absolutely-required-inputs.json +0 -315
- package/dist/ui-structures/reference/service-manifest-template.json +0 -342
- package/dist/version/VersionDetector.js +0 -723
- package/dist/worker/index.js +0 -4
- package/dist/worker/integration.js +0 -351
|
@@ -1,79 +0,0 @@
|
|
|
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
|
-
import { program } from 'commander';
|
|
13
|
-
import { ServiceInitializer } from "../../service-management/ServiceInitializer.js";
|
|
14
|
-
const SERVICE_TYPES = ['generic', 'data-service', 'auth-service', 'content-service', 'api-gateway'];
|
|
15
|
-
class ServiceInitializerCLI {
|
|
16
|
-
constructor() {
|
|
17
|
-
this.setupCLI();
|
|
18
|
-
}
|
|
19
|
-
setupCLI() {
|
|
20
|
-
program.name('clodo-init-service').description('Initialize a Clodo Framework service with auto-generated configurations').version('1.0.0').argument('<service-name>', 'Name of the service to initialize').option('-t, --type <type>', 'Service type', 'generic').option('-d, --domains <domains>', 'Comma-separated list of domains (can include account info)').option('-e, --env <environment>', 'Target environment', 'development').option('--api-token <token>', 'Cloudflare API token for domain discovery').option('--account-id <id>', 'Default Cloudflare account ID').option('--zone-id <id>', 'Default Cloudflare zone ID').option('-o, --output <path>', 'Output directory (services will be created in a services/ subdirectory)', process.cwd()).option('-f, --force', 'Overwrite existing service directory').option('--dry-run', 'Show what would be created without creating files').option('--multi-domain', 'Generate configurations for multiple domains').action((serviceName, options) => {
|
|
21
|
-
this.initializeService(serviceName, options);
|
|
22
|
-
});
|
|
23
|
-
program.on('--help', () => {
|
|
24
|
-
console.log('\nExamples:');
|
|
25
|
-
console.log(' clodo-init-service my-api --type api-gateway --domains "api.example.com,staging.example.com"');
|
|
26
|
-
console.log(' clodo-init-service data-service --type data-service --api-token $CF_TOKEN');
|
|
27
|
-
console.log(' clodo-init-service my-service --env production --account-id $CF_ACCOUNT_ID');
|
|
28
|
-
console.log('\nServices are created in: ./services/{service-name}/');
|
|
29
|
-
console.log('\nEnvironment Variables:');
|
|
30
|
-
console.log(' CLOUDFLARE_API_TOKEN - API token for domain discovery');
|
|
31
|
-
console.log(' CLOUDFLARE_ACCOUNT_ID - Account ID for configurations');
|
|
32
|
-
console.log(' CLOUDFLARE_ZONE_ID - Zone ID for domain configurations');
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
async initializeService(serviceName, options) {
|
|
36
|
-
try {
|
|
37
|
-
console.log('🚀 Clodo Framework - Service Initializer');
|
|
38
|
-
console.log('='.repeat(50));
|
|
39
|
-
console.log('📦 Using Clodo Framework ServiceInitializer module');
|
|
40
|
-
console.log('');
|
|
41
|
-
const initializer = new ServiceInitializer();
|
|
42
|
-
const result = await initializer.initializeService(serviceName, {
|
|
43
|
-
type: options.type,
|
|
44
|
-
domains: options.domains,
|
|
45
|
-
env: options.env,
|
|
46
|
-
apiToken: options.apiToken,
|
|
47
|
-
accountId: options.accountId,
|
|
48
|
-
zoneId: options.zoneId,
|
|
49
|
-
output: options.output,
|
|
50
|
-
force: options.force,
|
|
51
|
-
dryRun: options.dryRun
|
|
52
|
-
});
|
|
53
|
-
if (result.success) {
|
|
54
|
-
if (result.dryRun) {
|
|
55
|
-
console.log('📋 Dry run - would create the following:');
|
|
56
|
-
const configs = Array.isArray(result.configs) ? result.configs : [result.configs];
|
|
57
|
-
console.log('Files:', configs.filter(Boolean).join(', '));
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
console.log('\n✅ Service initialized successfully!');
|
|
61
|
-
console.log('\n📝 Next steps:');
|
|
62
|
-
console.log(` cd services/${serviceName}`);
|
|
63
|
-
console.log(' npm install');
|
|
64
|
-
console.log(' npm run dev # Start development server');
|
|
65
|
-
console.log(' npm run deploy # Deploy to Cloudflare');
|
|
66
|
-
} else {
|
|
67
|
-
console.error('\n❌ Initialization failed:', result.error);
|
|
68
|
-
process.exit(1);
|
|
69
|
-
}
|
|
70
|
-
} catch (error) {
|
|
71
|
-
console.error('\n❌ Unexpected error:', error.message);
|
|
72
|
-
process.exit(1);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Run the CLI
|
|
78
|
-
const cli = new ServiceInitializerCLI();
|
|
79
|
-
program.parse();
|
package/dist/config/customers.js
DELETED
|
@@ -1,623 +0,0 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, statSync } from 'fs';
|
|
2
|
-
// eslint-disable-next-line no-unused-vars
|
|
3
|
-
import { resolve, join } from 'path';
|
|
4
|
-
import toml from '@iarna/toml';
|
|
5
|
-
import { createDomainConfigSchema, validateDomainConfig, createDomainRegistry } from './domains.js';
|
|
6
|
-
import { getDirname } from '../utils/esm-helper.js';
|
|
7
|
-
const __dirname = getDirname(import.meta.url, 'src/config');
|
|
8
|
-
|
|
9
|
-
// Simple inline logger to avoid circular dependency with index.js
|
|
10
|
-
const logger = {
|
|
11
|
-
info: (message, ...args) => console.log(`[CustomerConfig] ${message}`, ...args),
|
|
12
|
-
error: (message, ...args) => console.error(`[CustomerConfig] ${message}`, ...args),
|
|
13
|
-
warn: (message, ...args) => console.warn(`[CustomerConfig] ${message}`, ...args),
|
|
14
|
-
debug: (message, ...args) => console.debug(`[CustomerConfig] ${message}`, ...args)
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Customer Configuration Manager
|
|
19
|
-
* Manages multi-environment, multi-customer configuration structure
|
|
20
|
-
* Integrates with existing domain and feature flag systems
|
|
21
|
-
*/
|
|
22
|
-
export class CustomerConfigurationManager {
|
|
23
|
-
constructor(options = {}) {
|
|
24
|
-
this.configDir = options.configDir || resolve(__dirname, '..', '..', 'config');
|
|
25
|
-
this.environments = options.environments || ['development', 'staging', 'production'];
|
|
26
|
-
this.domainRegistry = createDomainRegistry({});
|
|
27
|
-
this.customers = new Map();
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Create a new customer configuration from template
|
|
32
|
-
* @param {string} customerName - Customer identifier
|
|
33
|
-
* @param {string} domain - Customer domain (optional)
|
|
34
|
-
* @param {Object} options - Additional customer options
|
|
35
|
-
*/
|
|
36
|
-
async createCustomer(customerName, domain = null, options = {}) {
|
|
37
|
-
logger.info(`Creating customer configuration: ${customerName}`);
|
|
38
|
-
const customerDir = resolve(this.configDir, 'customers', customerName);
|
|
39
|
-
|
|
40
|
-
// Create customer directory structure
|
|
41
|
-
if (!existsSync(customerDir)) {
|
|
42
|
-
mkdirSync(customerDir, {
|
|
43
|
-
recursive: true
|
|
44
|
-
});
|
|
45
|
-
logger.info(`Created customer directory: ${customerDir}`);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Create domain configuration for customer
|
|
49
|
-
const domainConfig = this.createCustomerDomainConfig(customerName, domain, options);
|
|
50
|
-
this.domainRegistry.add(customerName, domainConfig);
|
|
51
|
-
|
|
52
|
-
// Create environment-specific configs from templates
|
|
53
|
-
for (const env of this.environments) {
|
|
54
|
-
await this.createCustomerEnvironment(customerName, env, domain, options);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Store customer metadata
|
|
58
|
-
this.customers.set(customerName, {
|
|
59
|
-
name: customerName,
|
|
60
|
-
domain: domain,
|
|
61
|
-
createdAt: new Date().toISOString(),
|
|
62
|
-
environments: this.environments,
|
|
63
|
-
...options
|
|
64
|
-
});
|
|
65
|
-
logger.info(`Customer ${customerName} configuration created successfully`);
|
|
66
|
-
return this.getCustomerInfo(customerName);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Create domain configuration for customer
|
|
71
|
-
*/
|
|
72
|
-
createCustomerDomainConfig(customerName, domain, options) {
|
|
73
|
-
const baseConfig = createDomainConfigSchema();
|
|
74
|
-
|
|
75
|
-
// Generate customer-specific domain config
|
|
76
|
-
const domainConfig = {
|
|
77
|
-
...baseConfig,
|
|
78
|
-
name: customerName,
|
|
79
|
-
displayName: options.displayName || this.capitalizeFirst(customerName),
|
|
80
|
-
accountId: options.accountId || '00000000000000000000000000000000',
|
|
81
|
-
// Placeholder for onboarding
|
|
82
|
-
zoneId: options.zoneId || '00000000000000000000000000000000',
|
|
83
|
-
// Placeholder for onboarding
|
|
84
|
-
|
|
85
|
-
domains: {
|
|
86
|
-
production: domain || `${customerName}.com`,
|
|
87
|
-
staging: `staging.${domain || `${customerName}.com`}`,
|
|
88
|
-
development: `dev.${domain || `${customerName}.com`}`
|
|
89
|
-
},
|
|
90
|
-
features: {
|
|
91
|
-
// Customer-specific feature flags
|
|
92
|
-
customerIsolation: true,
|
|
93
|
-
customDomain: !!domain,
|
|
94
|
-
multiEnvironment: true,
|
|
95
|
-
...options.features
|
|
96
|
-
},
|
|
97
|
-
settings: {
|
|
98
|
-
...baseConfig.settings,
|
|
99
|
-
customerName: customerName,
|
|
100
|
-
customerDomain: domain,
|
|
101
|
-
...options.settings
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
// Skip validation during initial creation if using placeholders
|
|
106
|
-
if (options.skipValidation || !options.accountId || !options.zoneId) {
|
|
107
|
-
logger.info(`Created customer domain config for ${customerName} (framework mode - placeholders used)`);
|
|
108
|
-
return domainConfig;
|
|
109
|
-
}
|
|
110
|
-
validateDomainConfig(domainConfig);
|
|
111
|
-
return domainConfig;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Create customer environment config from template
|
|
116
|
-
*/
|
|
117
|
-
// eslint-disable-next-line no-unused-vars
|
|
118
|
-
async createCustomerEnvironment(customerName, environment, domain, options) {
|
|
119
|
-
const templatePath = resolve(this.configDir, 'customers', 'template', `${environment}.env.template`);
|
|
120
|
-
const outputPath = resolve(this.configDir, 'customers', customerName, `${environment}.env`);
|
|
121
|
-
|
|
122
|
-
// Check if template exists
|
|
123
|
-
if (!existsSync(templatePath)) {
|
|
124
|
-
logger.warn(`Template not found: ${templatePath}, skipping ${environment} config`);
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Skip if config already exists
|
|
129
|
-
if (existsSync(outputPath)) {
|
|
130
|
-
logger.info(`Config already exists: ${outputPath}, skipping`);
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
try {
|
|
134
|
-
// Read template and replace placeholders
|
|
135
|
-
let template = readFileSync(templatePath, 'utf8');
|
|
136
|
-
|
|
137
|
-
// Replace customer-specific placeholders
|
|
138
|
-
template = template.replace(/\{\{CUSTOMER_NAME\}\}/g, customerName);
|
|
139
|
-
template = template.replace(/\{\{CUSTOMER_DOMAIN\}\}/g, domain || `${customerName}.com`);
|
|
140
|
-
template = template.replace(/\{\{ENVIRONMENT\}\}/g, environment);
|
|
141
|
-
|
|
142
|
-
// Environment-specific domain replacements
|
|
143
|
-
const envDomains = this.getEnvironmentDomains(customerName, domain);
|
|
144
|
-
template = template.replace(/\{\{DOMAIN\}\}/g, envDomains[environment]);
|
|
145
|
-
|
|
146
|
-
// Write customer config
|
|
147
|
-
writeFileSync(outputPath, template);
|
|
148
|
-
logger.info(`Created customer config: ${environment}.env for ${customerName}`);
|
|
149
|
-
} catch (error) {
|
|
150
|
-
logger.error(`Failed to create ${environment} config for ${customerName}:`, error.message);
|
|
151
|
-
throw error;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Get environment-specific domains for customer
|
|
157
|
-
*/
|
|
158
|
-
getEnvironmentDomains(customerName, domain) {
|
|
159
|
-
const baseDomain = domain || `${customerName}.com`;
|
|
160
|
-
return {
|
|
161
|
-
production: baseDomain,
|
|
162
|
-
staging: `staging.${baseDomain}`,
|
|
163
|
-
development: `dev.${baseDomain}`
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Validate customer configurations
|
|
169
|
-
*/
|
|
170
|
-
async validateConfigs() {
|
|
171
|
-
logger.info('Validating customer configuration structure...');
|
|
172
|
-
let valid = true;
|
|
173
|
-
const errors = [];
|
|
174
|
-
|
|
175
|
-
// Validate base configuration files
|
|
176
|
-
const baseFiles = ['wrangler.base.toml', 'variables.base.env'];
|
|
177
|
-
for (const file of baseFiles) {
|
|
178
|
-
const path = resolve(this.configDir, 'base', file);
|
|
179
|
-
if (!existsSync(path)) {
|
|
180
|
-
errors.push(`Missing base config: ${file}`);
|
|
181
|
-
valid = false;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Validate environment configs
|
|
186
|
-
for (const env of this.environments) {
|
|
187
|
-
const envFile = `${env}.toml`;
|
|
188
|
-
const path = resolve(this.configDir, 'environments', envFile);
|
|
189
|
-
if (!existsSync(path)) {
|
|
190
|
-
errors.push(`Missing environment config: ${envFile}`);
|
|
191
|
-
valid = false;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Validate customer configs
|
|
196
|
-
const customersDir = resolve(this.configDir, 'customers');
|
|
197
|
-
if (existsSync(customersDir)) {
|
|
198
|
-
const customerDirs = this.getCustomerDirectories();
|
|
199
|
-
for (const customerName of customerDirs) {
|
|
200
|
-
const customerValid = await this.validateCustomerConfig(customerName);
|
|
201
|
-
if (!customerValid) {
|
|
202
|
-
valid = false;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
if (errors.length > 0) {
|
|
207
|
-
logger.error('Configuration validation errors:', errors);
|
|
208
|
-
}
|
|
209
|
-
logger.info(`Configuration validation ${valid ? 'passed' : 'failed'}`);
|
|
210
|
-
return {
|
|
211
|
-
valid,
|
|
212
|
-
errors
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Validate individual customer configuration
|
|
218
|
-
*/
|
|
219
|
-
async validateCustomerConfig(customerName) {
|
|
220
|
-
const customerDir = resolve(this.configDir, 'customers', customerName);
|
|
221
|
-
let valid = true;
|
|
222
|
-
|
|
223
|
-
// Check environment files exist
|
|
224
|
-
for (const env of this.environments) {
|
|
225
|
-
const envFile = resolve(customerDir, `${env}.env`);
|
|
226
|
-
if (!existsSync(envFile)) {
|
|
227
|
-
logger.error(`Missing ${env}.env for customer ${customerName}`);
|
|
228
|
-
valid = false;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// Validate domain configuration (skip in framework mode)
|
|
233
|
-
try {
|
|
234
|
-
const domainConfig = this.domainRegistry.get(customerName);
|
|
235
|
-
if (!domainConfig.settings?.isFrameworkMode) {
|
|
236
|
-
validateDomainConfig(domainConfig);
|
|
237
|
-
} else {
|
|
238
|
-
logger.info(`Skipping domain validation for ${customerName} (framework mode)`);
|
|
239
|
-
}
|
|
240
|
-
} catch (error) {
|
|
241
|
-
logger.error(`Invalid domain config for ${customerName}:`, error.message);
|
|
242
|
-
valid = false;
|
|
243
|
-
}
|
|
244
|
-
return valid;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Show effective configuration for customer/environment
|
|
249
|
-
*/
|
|
250
|
-
showConfig(customerName, environment) {
|
|
251
|
-
logger.info(`Showing effective configuration: ${customerName}/${environment}`);
|
|
252
|
-
|
|
253
|
-
// Get domain config from registry
|
|
254
|
-
let domainConfig;
|
|
255
|
-
try {
|
|
256
|
-
domainConfig = this.domainRegistry.get(customerName);
|
|
257
|
-
} catch (error) {
|
|
258
|
-
// If not in registry, try to find customer metadata
|
|
259
|
-
const customerMeta = this.customers.get(customerName);
|
|
260
|
-
if (customerMeta) {
|
|
261
|
-
// Recreate domain config from metadata
|
|
262
|
-
domainConfig = this.createCustomerDomainConfig(customerName, customerMeta.domain, customerMeta);
|
|
263
|
-
} else {
|
|
264
|
-
throw new Error(`Customer not found: ${customerName}`);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
const config = {
|
|
268
|
-
customer: customerName,
|
|
269
|
-
environment: environment,
|
|
270
|
-
domain: domainConfig,
|
|
271
|
-
variables: {},
|
|
272
|
-
features: {}
|
|
273
|
-
};
|
|
274
|
-
|
|
275
|
-
// Load base variables
|
|
276
|
-
const baseVarsPath = resolve(this.configDir, 'base', 'variables.base.env');
|
|
277
|
-
if (existsSync(baseVarsPath)) {
|
|
278
|
-
config.variables.base = this.parseEnvFile(baseVarsPath);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Load customer environment variables
|
|
282
|
-
const customerConfigPath = resolve(this.configDir, 'customers', customerName, `${environment}.env`);
|
|
283
|
-
if (existsSync(customerConfigPath)) {
|
|
284
|
-
config.variables.customer = this.parseEnvFile(customerConfigPath);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// Get domain features
|
|
288
|
-
config.features = domainConfig.features || {};
|
|
289
|
-
return config;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Generate deployment command for customer/environment
|
|
294
|
-
*/
|
|
295
|
-
getDeployCommand(customerName, environment) {
|
|
296
|
-
const commands = {
|
|
297
|
-
development: `wrangler dev --config config/environments/development.toml`,
|
|
298
|
-
staging: `wrangler deploy --config config/environments/staging.toml --env staging`,
|
|
299
|
-
production: `wrangler deploy --env production`
|
|
300
|
-
};
|
|
301
|
-
const command = commands[environment];
|
|
302
|
-
if (!command) {
|
|
303
|
-
throw new Error(`Unknown environment: ${environment}`);
|
|
304
|
-
}
|
|
305
|
-
logger.info(`Generated deploy command for ${customerName}/${environment}`);
|
|
306
|
-
return {
|
|
307
|
-
command: command,
|
|
308
|
-
customer: customerName,
|
|
309
|
-
environment: environment,
|
|
310
|
-
configPath: `config/customers/${customerName}/${environment}.env`
|
|
311
|
-
};
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* Get customer information
|
|
316
|
-
*/
|
|
317
|
-
getCustomerInfo(customerName) {
|
|
318
|
-
const domainConfig = this.domainRegistry.get(customerName);
|
|
319
|
-
const customerMeta = this.customers.get(customerName);
|
|
320
|
-
return {
|
|
321
|
-
...customerMeta,
|
|
322
|
-
domainConfig: domainConfig,
|
|
323
|
-
configPath: resolve(this.configDir, 'customers', customerName)
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* List all customers
|
|
329
|
-
*/
|
|
330
|
-
listCustomers() {
|
|
331
|
-
return Array.from(this.customers.keys()).map(name => ({
|
|
332
|
-
name: name,
|
|
333
|
-
...this.getCustomerInfo(name)
|
|
334
|
-
}));
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* Load existing customers from filesystem
|
|
339
|
-
*/
|
|
340
|
-
async loadExistingCustomers() {
|
|
341
|
-
const customersDir = resolve(this.configDir, 'customers');
|
|
342
|
-
if (!existsSync(customersDir)) {
|
|
343
|
-
return;
|
|
344
|
-
}
|
|
345
|
-
try {
|
|
346
|
-
// First, try to read from root wrangler.toml (real deployment config)
|
|
347
|
-
const rootWranglerPath = resolve(this.configDir, '..', 'wrangler.toml');
|
|
348
|
-
let wranglerConfig = null;
|
|
349
|
-
let globalAccountId = null;
|
|
350
|
-
if (existsSync(rootWranglerPath)) {
|
|
351
|
-
try {
|
|
352
|
-
const wranglerContent = readFileSync(rootWranglerPath, 'utf8');
|
|
353
|
-
wranglerConfig = toml.parse(wranglerContent);
|
|
354
|
-
globalAccountId = wranglerConfig.account_id;
|
|
355
|
-
logger.info(`Loaded wrangler.toml with account_id: ${globalAccountId ? globalAccountId.substring(0, 8) + '...' : 'not found'}`);
|
|
356
|
-
} catch (error) {
|
|
357
|
-
logger.warn('Could not parse root wrangler.toml:', error.message);
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// Read customer directories
|
|
362
|
-
const customerDirs = this.getCustomerDirectories();
|
|
363
|
-
for (const customerName of customerDirs) {
|
|
364
|
-
// Skip template directory
|
|
365
|
-
if (customerName === 'template') continue;
|
|
366
|
-
const customerDir = resolve(customersDir, customerName);
|
|
367
|
-
|
|
368
|
-
// Initialize customer metadata
|
|
369
|
-
const metadata = {
|
|
370
|
-
name: customerName,
|
|
371
|
-
createdAt: new Date().toISOString(),
|
|
372
|
-
environments: this.environments,
|
|
373
|
-
accountId: globalAccountId // Start with global account ID
|
|
374
|
-
};
|
|
375
|
-
|
|
376
|
-
// Try to load from wrangler.toml [env.production] section
|
|
377
|
-
if (wranglerConfig && wranglerConfig.env && wranglerConfig.env.production) {
|
|
378
|
-
const prodEnv = wranglerConfig.env.production;
|
|
379
|
-
|
|
380
|
-
// Extract SERVICE_DOMAIN (maps to customer name usually)
|
|
381
|
-
if (prodEnv.vars && prodEnv.vars.SERVICE_DOMAIN) {
|
|
382
|
-
metadata.serviceDomain = prodEnv.vars.SERVICE_DOMAIN;
|
|
383
|
-
|
|
384
|
-
// If SERVICE_DOMAIN matches customer name, this is their config
|
|
385
|
-
if (prodEnv.vars.SERVICE_DOMAIN === customerName) {
|
|
386
|
-
// Extract database info
|
|
387
|
-
if (prodEnv.d1_databases && prodEnv.d1_databases.length > 0) {
|
|
388
|
-
const db = prodEnv.d1_databases[0];
|
|
389
|
-
metadata.databaseId = db.database_id;
|
|
390
|
-
metadata.databaseName = db.database_name;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// Extract zone_id if present
|
|
394
|
-
if (prodEnv.zone_id) {
|
|
395
|
-
metadata.zoneId = prodEnv.zone_id;
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
// Extract route to infer domain
|
|
399
|
-
if (prodEnv.route) {
|
|
400
|
-
// Route format: "example.com/*" or "*.example.com/*"
|
|
401
|
-
const domain = prodEnv.route.replace(/\/\*$/, '').replace(/^\*\./, '');
|
|
402
|
-
metadata.domain = domain;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
// Read customer-specific env file to get CUSTOMER_DOMAIN and other info
|
|
409
|
-
const prodConfigPath = resolve(customerDir, 'production.env');
|
|
410
|
-
if (existsSync(prodConfigPath)) {
|
|
411
|
-
try {
|
|
412
|
-
const prodConfig = this.parseEnvFile(prodConfigPath);
|
|
413
|
-
|
|
414
|
-
// Use CUSTOMER_DOMAIN (correct field name in real configs)
|
|
415
|
-
if (prodConfig.CUSTOMER_DOMAIN) {
|
|
416
|
-
metadata.customerDomain = prodConfig.CUSTOMER_DOMAIN.replace(/^https?:\/\//, '');
|
|
417
|
-
// If we didn't get domain from wrangler.toml, use this
|
|
418
|
-
if (!metadata.domain) {
|
|
419
|
-
metadata.domain = metadata.customerDomain;
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
// Also check old DOMAIN field for backward compatibility
|
|
424
|
-
if (!metadata.domain && prodConfig.DOMAIN) {
|
|
425
|
-
metadata.domain = prodConfig.DOMAIN.replace(/^https?:\/\//, '');
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
// Extract customer ID
|
|
429
|
-
if (prodConfig.CUSTOMER_ID) {
|
|
430
|
-
metadata.customerId = prodConfig.CUSTOMER_ID;
|
|
431
|
-
}
|
|
432
|
-
} catch (error) {
|
|
433
|
-
logger.warn(`Could not parse customer env for ${customerName}:`, error.message);
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
// Check if secrets exist (we can't read them, but can note they should be set)
|
|
438
|
-
// In a real system, you'd run: wrangler secret list --env production
|
|
439
|
-
// For now, we just note that secrets should be managed separately
|
|
440
|
-
metadata.hasSecrets = true; // Assume secrets are managed via wrangler secret commands
|
|
441
|
-
|
|
442
|
-
// Register customer
|
|
443
|
-
this.customers.set(customerName, metadata);
|
|
444
|
-
|
|
445
|
-
// Try to register domain config
|
|
446
|
-
try {
|
|
447
|
-
const domainConfig = this.createCustomerDomainConfig(customerName, metadata.domain, {
|
|
448
|
-
skipValidation: true,
|
|
449
|
-
isFrameworkMode: true,
|
|
450
|
-
accountId: metadata.accountId,
|
|
451
|
-
zoneId: metadata.zoneId
|
|
452
|
-
});
|
|
453
|
-
this.domainRegistry.add(customerName, domainConfig);
|
|
454
|
-
} catch (error) {
|
|
455
|
-
logger.warn(`Could not register domain for existing customer ${customerName}:`, error.message);
|
|
456
|
-
}
|
|
457
|
-
logger.info(`Loaded existing customer: ${customerName}`);
|
|
458
|
-
}
|
|
459
|
-
} catch (error) {
|
|
460
|
-
logger.error('Error loading existing customers:', error.message);
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
/**
|
|
465
|
-
* Get customer directories from filesystem
|
|
466
|
-
*/
|
|
467
|
-
getCustomerDirectories() {
|
|
468
|
-
const customersDir = resolve(this.configDir, 'customers');
|
|
469
|
-
if (!existsSync(customersDir)) {
|
|
470
|
-
return [];
|
|
471
|
-
}
|
|
472
|
-
try {
|
|
473
|
-
// Read directory contents
|
|
474
|
-
const items = readdirSync(customersDir);
|
|
475
|
-
return items.filter(item => {
|
|
476
|
-
const itemPath = resolve(customersDir, item);
|
|
477
|
-
return statSync(itemPath).isDirectory() && item !== 'template';
|
|
478
|
-
});
|
|
479
|
-
} catch (error) {
|
|
480
|
-
logger.error('Error reading customer directories:', error.message);
|
|
481
|
-
return [];
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
/**
|
|
486
|
-
* Parse environment file
|
|
487
|
-
*/
|
|
488
|
-
parseEnvFile(filePath) {
|
|
489
|
-
const content = readFileSync(filePath, 'utf8');
|
|
490
|
-
const variables = {};
|
|
491
|
-
content.split('\n').forEach(line => {
|
|
492
|
-
const trimmed = line.trim();
|
|
493
|
-
if (trimmed && !trimmed.startsWith('#')) {
|
|
494
|
-
const [key, ...valueParts] = trimmed.split('=');
|
|
495
|
-
if (key && valueParts.length > 0) {
|
|
496
|
-
variables[key.trim()] = valueParts.join('=').trim();
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
});
|
|
500
|
-
return variables;
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
/**
|
|
504
|
-
* Update wrangler.toml with new configuration
|
|
505
|
-
* @param {string} wranglerPath - Path to wrangler.toml
|
|
506
|
-
* @param {Object} updates - Configuration updates to merge
|
|
507
|
-
* @returns {boolean} Success status
|
|
508
|
-
*/
|
|
509
|
-
updateWranglerToml(wranglerPath, updates) {
|
|
510
|
-
try {
|
|
511
|
-
let existingConfig = {};
|
|
512
|
-
|
|
513
|
-
// Read existing config if file exists
|
|
514
|
-
if (existsSync(wranglerPath)) {
|
|
515
|
-
const content = readFileSync(wranglerPath, 'utf8');
|
|
516
|
-
existingConfig = toml.parse(content);
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
// Deep merge updates into existing config
|
|
520
|
-
const mergedConfig = this.deepMergeConfig(existingConfig, updates);
|
|
521
|
-
|
|
522
|
-
// Write back to file
|
|
523
|
-
const tomlContent = toml.stringify(mergedConfig);
|
|
524
|
-
writeFileSync(wranglerPath, tomlContent, 'utf8');
|
|
525
|
-
logger.info(`Updated wrangler.toml: ${wranglerPath}`);
|
|
526
|
-
return true;
|
|
527
|
-
} catch (error) {
|
|
528
|
-
logger.error(`Failed to update wrangler.toml: ${error.message}`);
|
|
529
|
-
return false;
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
/**
|
|
534
|
-
* Deep merge two configuration objects
|
|
535
|
-
* @private
|
|
536
|
-
*/
|
|
537
|
-
deepMergeConfig(target, source) {
|
|
538
|
-
const output = {
|
|
539
|
-
...target
|
|
540
|
-
};
|
|
541
|
-
for (const key in source) {
|
|
542
|
-
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
|
543
|
-
// Recursively merge objects
|
|
544
|
-
output[key] = this.deepMergeConfig(target[key] || {}, source[key]);
|
|
545
|
-
} else {
|
|
546
|
-
// Overwrite arrays and primitives
|
|
547
|
-
output[key] = source[key];
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
return output;
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
/**
|
|
554
|
-
* Create or update environment section in wrangler.toml
|
|
555
|
-
* @param {string} wranglerPath - Path to wrangler.toml
|
|
556
|
-
* @param {string} environment - Environment name (production, staging, development)
|
|
557
|
-
* @param {Object} envConfig - Environment configuration
|
|
558
|
-
* @returns {boolean} Success status
|
|
559
|
-
*/
|
|
560
|
-
updateEnvironmentConfig(wranglerPath, environment, envConfig) {
|
|
561
|
-
const updates = {
|
|
562
|
-
env: {
|
|
563
|
-
[environment]: envConfig
|
|
564
|
-
}
|
|
565
|
-
};
|
|
566
|
-
return this.updateWranglerToml(wranglerPath, updates);
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
/**
|
|
570
|
-
* Add D1 database binding to environment
|
|
571
|
-
* @param {string} wranglerPath - Path to wrangler.toml
|
|
572
|
-
* @param {string} environment - Environment name
|
|
573
|
-
* @param {Object} databaseConfig - Database configuration
|
|
574
|
-
* @returns {boolean} Success status
|
|
575
|
-
*/
|
|
576
|
-
addD1Database(wranglerPath, environment, databaseConfig) {
|
|
577
|
-
try {
|
|
578
|
-
const content = readFileSync(wranglerPath, 'utf8');
|
|
579
|
-
const config = toml.parse(content);
|
|
580
|
-
|
|
581
|
-
// Ensure env section exists
|
|
582
|
-
if (!config.env) config.env = {};
|
|
583
|
-
if (!config.env[environment]) config.env[environment] = {};
|
|
584
|
-
|
|
585
|
-
// Ensure d1_databases array exists
|
|
586
|
-
if (!config.env[environment].d1_databases) {
|
|
587
|
-
config.env[environment].d1_databases = [];
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
// Add or update database
|
|
591
|
-
const existingIndex = config.env[environment].d1_databases.findIndex(db => db.binding === databaseConfig.binding);
|
|
592
|
-
if (existingIndex >= 0) {
|
|
593
|
-
config.env[environment].d1_databases[existingIndex] = databaseConfig;
|
|
594
|
-
} else {
|
|
595
|
-
config.env[environment].d1_databases.push(databaseConfig);
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
// Write back
|
|
599
|
-
writeFileSync(wranglerPath, toml.stringify(config), 'utf8');
|
|
600
|
-
logger.info(`Added D1 database to ${environment} environment`);
|
|
601
|
-
return true;
|
|
602
|
-
} catch (error) {
|
|
603
|
-
logger.error(`Failed to add D1 database: ${error.message}`);
|
|
604
|
-
return false;
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
/**
|
|
609
|
-
* Capitalize first letter
|
|
610
|
-
*/
|
|
611
|
-
capitalizeFirst(str) {
|
|
612
|
-
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
// Default instance
|
|
617
|
-
export const customerConfigManager = new CustomerConfigurationManager();
|
|
618
|
-
|
|
619
|
-
// Convenience functions
|
|
620
|
-
export const createCustomer = (name, domain, options) => customerConfigManager.createCustomer(name, domain, options);
|
|
621
|
-
export const validateCustomerConfigs = () => customerConfigManager.validateConfigs();
|
|
622
|
-
export const showCustomerConfig = (customer, env) => customerConfigManager.showConfig(customer, env);
|
|
623
|
-
export const getCustomerDeployCommand = (customer, env) => customerConfigManager.getDeployCommand(customer, env);
|