@aifabrix/builder 2.31.1 → 2.32.1
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/README.md +9 -9
- package/integration/hubspot/README.md +2 -2
- package/integration/hubspot/hubspot-deploy-company.json +17 -14
- package/integration/hubspot/hubspot-deploy-contact.json +19 -16
- package/integration/hubspot/hubspot-deploy-deal.json +21 -18
- package/lib/api/types/datasources.types.js +31 -5
- package/lib/api/types/wizard.types.js +142 -0
- package/lib/api/wizard.api.js +177 -0
- package/lib/{app-config.js → app/config.js} +4 -4
- package/lib/{app-deploy.js → app/deploy.js} +8 -8
- package/lib/app/display.js +90 -0
- package/lib/{app-dockerfile.js → app/dockerfile.js} +4 -4
- package/lib/{app-down.js → app/down.js} +4 -4
- package/lib/app/helpers.js +218 -0
- package/lib/app/index.js +298 -0
- package/lib/{app-list.js → app/list.js} +6 -6
- package/lib/{app-push.js → app/push.js} +4 -4
- package/lib/{app-readme.js → app/readme.js} +34 -13
- package/lib/{app-register.js → app/register.js} +9 -9
- package/lib/{app-rotate-secret.js → app/rotate-secret.js} +10 -10
- package/lib/{app-run-helpers.js → app/run-helpers.js} +10 -10
- package/lib/{app-run.js → app/run.js} +6 -6
- package/lib/{build.js → build/index.js} +59 -32
- package/lib/build/package.json +7 -0
- package/lib/cli.js +245 -179
- package/lib/commands/app.js +3 -3
- package/lib/commands/datasource.js +4 -4
- package/lib/commands/login-credentials.js +209 -0
- package/lib/commands/login-device.js +254 -0
- package/lib/commands/login.js +67 -378
- package/lib/commands/logout.js +1 -1
- package/lib/commands/secrets-set.js +1 -1
- package/lib/commands/secure.js +2 -2
- package/lib/commands/wizard.js +498 -0
- package/lib/{audit-logger.js → core/audit-logger.js} +1 -1
- package/lib/{config.js → core/config.js} +28 -26
- package/lib/{diff.js → core/diff.js} +157 -72
- package/lib/{secrets.js → core/secrets.js} +86 -49
- package/lib/{templates.js → core/templates-env.js} +14 -222
- package/lib/core/templates.js +279 -0
- package/lib/{datasource-deploy.js → datasource/deploy.js} +6 -6
- package/lib/{datasource-diff.js → datasource/diff.js} +2 -2
- package/lib/datasource/list.js +223 -0
- package/lib/{datasource-validate.js → datasource/validate.js} +2 -2
- package/lib/{deployer.js → deployment/deployer.js} +48 -18
- package/lib/{environment-deploy.js → deployment/environment.js} +163 -84
- package/lib/{push.js → deployment/push.js} +1 -1
- package/lib/external-system/deploy-helpers.js +145 -0
- package/lib/{external-system-deploy.js → external-system/deploy.js} +156 -111
- package/lib/external-system/download-helpers.js +114 -0
- package/lib/{external-system-download.js → external-system/download.js} +92 -135
- package/lib/{external-system-generator.js → external-system/generator.js} +15 -11
- package/lib/external-system/test-auth.js +40 -0
- package/lib/external-system/test-execution.js +84 -0
- package/lib/external-system/test-helpers.js +109 -0
- package/lib/{external-system-test.js → external-system/test.js} +174 -192
- package/lib/{generator-builders.js → generator/builders.js} +87 -10
- package/lib/{generator-external.js → generator/external.js} +115 -52
- package/lib/{github-generator.js → generator/github.js} +116 -15
- package/lib/{generator-helpers.js → generator/helpers.js} +92 -42
- package/lib/{generator.js → generator/index.js} +49 -22
- package/lib/{generator-split.js → generator/split.js} +108 -55
- package/lib/generator/wizard-prompts.js +357 -0
- package/lib/generator/wizard.js +490 -0
- package/lib/{infra.js → infrastructure/index.js} +49 -22
- package/lib/schema/external-datasource.schema.json +145 -133
- package/lib/schema/external-system.schema.json +42 -0
- package/lib/utils/api.js +9 -5
- package/lib/utils/app-register-api.js +60 -32
- package/lib/utils/app-register-auth.js +172 -47
- package/lib/utils/app-register-config.js +130 -59
- package/lib/utils/app-run-containers.js +29 -8
- package/lib/utils/build-helpers.js +1 -1
- package/lib/utils/cli-utils.js +78 -30
- package/lib/utils/compose-generator.js +145 -65
- package/lib/utils/config-paths.js +2 -0
- package/lib/utils/deployment-errors.js +1 -1
- package/lib/utils/device-code.js +99 -41
- package/lib/utils/env-config-loader.js +1 -1
- package/lib/utils/env-copy.js +21 -18
- package/lib/utils/env-endpoints.js +115 -67
- package/lib/utils/env-map.js +13 -14
- package/lib/utils/env-ports.js +45 -25
- package/lib/utils/env-template.js +84 -42
- package/lib/utils/error-formatter.js +26 -9
- package/lib/utils/error-formatters/error-parser.js +90 -4
- package/lib/utils/error-formatters/http-status-errors.js +54 -17
- package/lib/utils/error-formatters/network-errors.js +103 -26
- package/lib/utils/external-system-display.js +184 -90
- package/lib/utils/external-system-validators.js +164 -42
- package/lib/utils/file-upload.js +109 -0
- package/lib/utils/health-check.js +199 -83
- package/lib/utils/infra-containers.js +1 -1
- package/lib/utils/infra-status.js +66 -15
- package/lib/utils/local-secrets.js +45 -25
- package/lib/utils/paths.js +45 -33
- package/lib/utils/schema-loader.js +42 -25
- package/lib/utils/schema-resolver.js +123 -74
- package/lib/utils/secrets-encryption.js +62 -25
- package/lib/utils/secrets-helpers.js +126 -63
- package/lib/utils/secrets-path.js +1 -1
- package/lib/utils/secrets-url.js +1 -1
- package/lib/utils/token-manager-refresh.js +181 -0
- package/lib/utils/token-manager.js +76 -123
- package/lib/utils/variable-transformer.js +154 -77
- package/lib/utils/yaml-preserve.js +41 -47
- package/lib/{template-validator.js → validation/template.js} +54 -23
- package/lib/{validate.js → validation/validate.js} +205 -125
- package/lib/{validator.js → validation/validator.js} +58 -39
- package/package.json +31 -2
- package/templates/external-system/deploy.ps1.hbs +34 -0
- package/templates/external-system/deploy.sh.hbs +34 -0
- package/templates/external-system/external-datasource.json.hbs +31 -12
- package/lib/app.js +0 -467
- package/lib/datasource-list.js +0 -141
- /package/lib/{app-prompts.js → app/prompts.js} +0 -0
- /package/lib/{env-reader.js → core/env-reader.js} +0 -0
- /package/lib/{key-generator.js → core/key-generator.js} +0 -0
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Wizard file generator - saves dataplane-generated configurations to files
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 2.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs').promises;
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const yaml = require('js-yaml');
|
|
10
|
+
const Handlebars = require('handlebars');
|
|
11
|
+
const chalk = require('chalk');
|
|
12
|
+
const logger = require('../utils/logger');
|
|
13
|
+
const { generateExternalSystemApplicationSchema } = require('./external');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Generate files from dataplane-generated wizard configurations
|
|
17
|
+
* @async
|
|
18
|
+
* @function generateWizardFiles
|
|
19
|
+
* @param {string} appName - Application name
|
|
20
|
+
* @param {Object} systemConfig - System configuration from dataplane
|
|
21
|
+
* @param {Object[]} datasourceConfigs - Array of datasource configurations from dataplane
|
|
22
|
+
* @param {string} systemKey - System key (from dataplane or derived)
|
|
23
|
+
* @returns {Promise<Object>} Object with generated file paths
|
|
24
|
+
* @throws {Error} If file generation fails
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* Writes system JSON file
|
|
28
|
+
* @async
|
|
29
|
+
* @function writeSystemJsonFile
|
|
30
|
+
* @param {string} appPath - Application path
|
|
31
|
+
* @param {string} finalSystemKey - Final system key
|
|
32
|
+
* @param {Object} systemConfig - System configuration
|
|
33
|
+
* @returns {Promise<string>} System file path
|
|
34
|
+
*/
|
|
35
|
+
async function writeSystemJsonFile(appPath, finalSystemKey, systemConfig) {
|
|
36
|
+
const systemFileName = `${finalSystemKey}-deploy.json`;
|
|
37
|
+
const systemFilePath = path.join(appPath, systemFileName);
|
|
38
|
+
await fs.writeFile(systemFilePath, JSON.stringify(systemConfig, null, 2), 'utf8');
|
|
39
|
+
logger.log(chalk.green(`✓ Generated system file: ${systemFileName}`));
|
|
40
|
+
return systemFilePath;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Writes datasource JSON files
|
|
45
|
+
* @async
|
|
46
|
+
* @function writeDatasourceJsonFiles
|
|
47
|
+
* @param {string} appPath - Application path
|
|
48
|
+
* @param {string} finalSystemKey - Final system key
|
|
49
|
+
* @param {Object[]} datasourceConfigs - Array of datasource configurations
|
|
50
|
+
* @returns {Promise<string[]>} Array of datasource file names
|
|
51
|
+
*/
|
|
52
|
+
async function writeDatasourceJsonFiles(appPath, finalSystemKey, datasourceConfigs) {
|
|
53
|
+
const datasourceFileNames = [];
|
|
54
|
+
for (const datasourceConfig of datasourceConfigs) {
|
|
55
|
+
const entityType = datasourceConfig.entityType || datasourceConfig.entityKey || datasourceConfig.key?.split('-').pop() || 'default';
|
|
56
|
+
const datasourceFileName = `${finalSystemKey}-deploy-${entityType}.json`;
|
|
57
|
+
const datasourceFilePath = path.join(appPath, datasourceFileName);
|
|
58
|
+
await fs.writeFile(datasourceFilePath, JSON.stringify(datasourceConfig, null, 2), 'utf8');
|
|
59
|
+
datasourceFileNames.push(datasourceFileName);
|
|
60
|
+
logger.log(chalk.green(`✓ Generated datasource file: ${datasourceFileName}`));
|
|
61
|
+
}
|
|
62
|
+
return datasourceFileNames;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Generates all configuration files
|
|
67
|
+
* @async
|
|
68
|
+
* @function generateConfigFilesForWizard
|
|
69
|
+
* @param {Object} params - Parameters object
|
|
70
|
+
* @param {string} params.appPath - Application path
|
|
71
|
+
* @param {string} params.appName - Application name
|
|
72
|
+
* @param {string} params.finalSystemKey - Final system key
|
|
73
|
+
* @param {string} params.systemFileName - System file name
|
|
74
|
+
* @param {string[]} params.datasourceFileNames - Array of datasource file names
|
|
75
|
+
* @param {Object} params.systemConfig - System configuration
|
|
76
|
+
* @param {Object[]} params.datasourceConfigs - Array of datasource configurations
|
|
77
|
+
* @param {string} [params.aiGeneratedReadme] - Optional AI-generated README content
|
|
78
|
+
* @returns {Promise<Object>} Object with file paths
|
|
79
|
+
*/
|
|
80
|
+
async function generateConfigFilesForWizard(params) {
|
|
81
|
+
const { appPath, appName, finalSystemKey, systemFileName, datasourceFileNames, systemConfig, datasourceConfigs, aiGeneratedReadme } = params;
|
|
82
|
+
|
|
83
|
+
// Generate or update variables.yaml with externalIntegration block
|
|
84
|
+
await generateOrUpdateVariablesYaml({
|
|
85
|
+
appPath,
|
|
86
|
+
appName,
|
|
87
|
+
systemKey: finalSystemKey,
|
|
88
|
+
systemFileName,
|
|
89
|
+
datasourceFileNames,
|
|
90
|
+
systemConfig
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Generate env.template with authentication variables
|
|
94
|
+
await generateEnvTemplate(appPath, systemConfig);
|
|
95
|
+
|
|
96
|
+
// Generate README.md (use AI-generated content if available)
|
|
97
|
+
await generateReadme(appPath, appName, finalSystemKey, systemConfig, datasourceConfigs, aiGeneratedReadme);
|
|
98
|
+
|
|
99
|
+
// Generate deployment scripts
|
|
100
|
+
const deployScripts = await generateDeployScripts(appPath, finalSystemKey, systemFileName, datasourceFileNames);
|
|
101
|
+
|
|
102
|
+
// Generate application-schema.json
|
|
103
|
+
const applicationSchema = await generateExternalSystemApplicationSchema(appName);
|
|
104
|
+
const applicationSchemaPath = path.join(appPath, 'application-schema.json');
|
|
105
|
+
await fs.writeFile(applicationSchemaPath, JSON.stringify(applicationSchema, null, 2), 'utf8');
|
|
106
|
+
logger.log(chalk.green('✓ Generated application-schema.json'));
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
variablesPath: path.join(appPath, 'variables.yaml'),
|
|
110
|
+
envTemplatePath: path.join(appPath, 'env.template'),
|
|
111
|
+
readmePath: path.join(appPath, 'README.md'),
|
|
112
|
+
applicationSchemaPath,
|
|
113
|
+
...deployScripts
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function generateWizardFiles(appName, systemConfig, datasourceConfigs, systemKey, options = {}) {
|
|
118
|
+
try {
|
|
119
|
+
const { aiGeneratedReadme } = options || {};
|
|
120
|
+
// Determine app path (integration directory for external systems)
|
|
121
|
+
const appPath = path.join(process.cwd(), 'integration', appName);
|
|
122
|
+
|
|
123
|
+
// Create directory if it doesn't exist
|
|
124
|
+
await fs.mkdir(appPath, { recursive: true });
|
|
125
|
+
|
|
126
|
+
// Extract system key from config if not provided
|
|
127
|
+
const finalSystemKey = systemKey || systemConfig.key || appName;
|
|
128
|
+
|
|
129
|
+
// Write system and datasource JSON files
|
|
130
|
+
const systemFilePath = await writeSystemJsonFile(appPath, finalSystemKey, systemConfig);
|
|
131
|
+
const datasourceFileNames = await writeDatasourceJsonFiles(appPath, finalSystemKey, datasourceConfigs);
|
|
132
|
+
|
|
133
|
+
// Generate configuration files
|
|
134
|
+
const systemFileName = `${finalSystemKey}-deploy.json`;
|
|
135
|
+
const configFiles = await generateConfigFilesForWizard({
|
|
136
|
+
appPath,
|
|
137
|
+
appName,
|
|
138
|
+
finalSystemKey,
|
|
139
|
+
systemFileName,
|
|
140
|
+
datasourceFileNames,
|
|
141
|
+
systemConfig,
|
|
142
|
+
datasourceConfigs,
|
|
143
|
+
aiGeneratedReadme
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
appPath,
|
|
148
|
+
systemFilePath,
|
|
149
|
+
datasourceFilePaths: datasourceFileNames.map(name => path.join(appPath, name)),
|
|
150
|
+
...configFiles
|
|
151
|
+
};
|
|
152
|
+
} catch (error) {
|
|
153
|
+
throw new Error(`Failed to generate wizard files: ${error.message}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Generate or update variables.yaml with externalIntegration block
|
|
159
|
+
* @async
|
|
160
|
+
* @function generateOrUpdateVariablesYaml
|
|
161
|
+
* @param {Object} params - Parameters object
|
|
162
|
+
* @param {string} params.appPath - Application directory path
|
|
163
|
+
* @param {string} params.appName - Application name
|
|
164
|
+
* @param {string} params.systemKey - System key
|
|
165
|
+
* @param {string} params.systemFileName - System file name
|
|
166
|
+
* @param {string[]} params.datasourceFileNames - Array of datasource file names
|
|
167
|
+
* @param {Object} params.systemConfig - System configuration
|
|
168
|
+
* @throws {Error} If generation fails
|
|
169
|
+
*/
|
|
170
|
+
async function generateOrUpdateVariablesYaml(params) {
|
|
171
|
+
const { appPath, appName, systemFileName, datasourceFileNames, systemConfig } = params;
|
|
172
|
+
try {
|
|
173
|
+
const variablesPath = path.join(appPath, 'variables.yaml');
|
|
174
|
+
let variables = {};
|
|
175
|
+
|
|
176
|
+
// Try to read existing variables.yaml
|
|
177
|
+
try {
|
|
178
|
+
const existingContent = await fs.readFile(variablesPath, 'utf8');
|
|
179
|
+
variables = yaml.load(existingContent) || {};
|
|
180
|
+
} catch (error) {
|
|
181
|
+
// File doesn't exist, create new one
|
|
182
|
+
if (error.code !== 'ENOENT') {
|
|
183
|
+
throw error;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Set basic app info if not present
|
|
188
|
+
if (!variables.app) {
|
|
189
|
+
variables.app = {
|
|
190
|
+
key: appName,
|
|
191
|
+
displayName: systemConfig.displayName || appName.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase()),
|
|
192
|
+
description: systemConfig.description || `External system integration for ${appName}`,
|
|
193
|
+
type: 'external'
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Set deployment config if not present
|
|
198
|
+
if (!variables.deployment) {
|
|
199
|
+
variables.deployment = {
|
|
200
|
+
controllerUrl: '',
|
|
201
|
+
environment: 'dev'
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Add or update externalIntegration block
|
|
206
|
+
variables.externalIntegration = {
|
|
207
|
+
schemaBasePath: './',
|
|
208
|
+
systems: [systemFileName],
|
|
209
|
+
dataSources: datasourceFileNames,
|
|
210
|
+
autopublish: true,
|
|
211
|
+
version: systemConfig.version || '1.0.0'
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
await fs.writeFile(variablesPath, yaml.dump(variables, { indent: 2, lineWidth: 120, noRefs: true }), 'utf8');
|
|
215
|
+
logger.log(chalk.green('✓ Generated/updated variables.yaml'));
|
|
216
|
+
} catch (error) {
|
|
217
|
+
throw new Error(`Failed to generate variables.yaml: ${error.message}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Adds API key authentication lines to env template
|
|
223
|
+
* @function addApiKeyAuthLines
|
|
224
|
+
* @param {Array<string>} lines - Lines array to append to
|
|
225
|
+
*/
|
|
226
|
+
function addApiKeyAuthLines(lines) {
|
|
227
|
+
lines.push('# API Key Authentication');
|
|
228
|
+
lines.push('API_KEY=kv://secrets/api-key');
|
|
229
|
+
lines.push('');
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Adds OAuth2 authentication lines to env template
|
|
234
|
+
* @function addOAuth2AuthLines
|
|
235
|
+
* @param {Array<string>} lines - Lines array to append to
|
|
236
|
+
* @param {Object} auth - Authentication configuration
|
|
237
|
+
*/
|
|
238
|
+
function addOAuth2AuthLines(lines, auth) {
|
|
239
|
+
lines.push('# OAuth2 Authentication');
|
|
240
|
+
lines.push('CLIENT_ID=kv://secrets/client-id');
|
|
241
|
+
lines.push('CLIENT_SECRET=kv://secrets/client-secret');
|
|
242
|
+
lines.push('AUTH_URL=kv://secrets/auth-url');
|
|
243
|
+
lines.push('TOKEN_URL=kv://secrets/token-url');
|
|
244
|
+
if (auth.scope) {
|
|
245
|
+
lines.push(`SCOPE=${auth.scope}`);
|
|
246
|
+
}
|
|
247
|
+
lines.push('');
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Adds bearer token authentication lines to env template
|
|
252
|
+
* @function addBearerTokenAuthLines
|
|
253
|
+
* @param {Array<string>} lines - Lines array to append to
|
|
254
|
+
*/
|
|
255
|
+
function addBearerTokenAuthLines(lines) {
|
|
256
|
+
lines.push('# Bearer Token Authentication');
|
|
257
|
+
lines.push('BEARER_TOKEN=kv://secrets/bearer-token');
|
|
258
|
+
lines.push('');
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Adds basic authentication lines to env template
|
|
263
|
+
* @function addBasicAuthLines
|
|
264
|
+
* @param {Array<string>} lines - Lines array to append to
|
|
265
|
+
*/
|
|
266
|
+
function addBasicAuthLines(lines) {
|
|
267
|
+
lines.push('# Basic Authentication');
|
|
268
|
+
lines.push('USERNAME=kv://secrets/username');
|
|
269
|
+
lines.push('PASSWORD=kv://secrets/password');
|
|
270
|
+
lines.push('');
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Adds authentication lines based on auth type
|
|
275
|
+
* @function addAuthenticationLines
|
|
276
|
+
* @param {Array<string>} lines - Lines array to append to
|
|
277
|
+
* @param {Object} auth - Authentication configuration
|
|
278
|
+
* @param {string} authType - Authentication type
|
|
279
|
+
*/
|
|
280
|
+
function addAuthenticationLines(lines, auth, authType) {
|
|
281
|
+
if (authType === 'apikey' || authType === 'apiKey') {
|
|
282
|
+
addApiKeyAuthLines(lines);
|
|
283
|
+
} else if (authType === 'oauth2' || authType === 'oauth') {
|
|
284
|
+
addOAuth2AuthLines(lines, auth);
|
|
285
|
+
} else if (authType === 'bearer' || authType === 'token') {
|
|
286
|
+
addBearerTokenAuthLines(lines);
|
|
287
|
+
} else if (authType === 'basic') {
|
|
288
|
+
addBasicAuthLines(lines);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Adds base URL lines if present
|
|
294
|
+
* @function addBaseUrlLines
|
|
295
|
+
* @param {Array<string>} lines - Lines array to append to
|
|
296
|
+
* @param {Object} systemConfig - System configuration
|
|
297
|
+
*/
|
|
298
|
+
function addBaseUrlLines(lines, systemConfig) {
|
|
299
|
+
if (systemConfig.baseUrl || systemConfig.baseURL) {
|
|
300
|
+
lines.push('# API Base URL');
|
|
301
|
+
lines.push(`BASE_URL=${systemConfig.baseUrl || systemConfig.baseURL}`);
|
|
302
|
+
lines.push('');
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Generate env.template with authentication variables
|
|
308
|
+
* @async
|
|
309
|
+
* @function generateEnvTemplate
|
|
310
|
+
* @param {string} appPath - Application directory path
|
|
311
|
+
* @param {Object} systemConfig - System configuration
|
|
312
|
+
* @throws {Error} If generation fails
|
|
313
|
+
*/
|
|
314
|
+
async function generateEnvTemplate(appPath, systemConfig) {
|
|
315
|
+
try {
|
|
316
|
+
const envTemplatePath = path.join(appPath, 'env.template');
|
|
317
|
+
const lines = ['# Environment variables for external system integration', ''];
|
|
318
|
+
|
|
319
|
+
// Extract authentication variables from system config
|
|
320
|
+
const auth = systemConfig.authentication || systemConfig.auth || {};
|
|
321
|
+
const authType = auth.type || auth.authType || 'apikey';
|
|
322
|
+
|
|
323
|
+
addAuthenticationLines(lines, auth, authType);
|
|
324
|
+
addBaseUrlLines(lines, systemConfig);
|
|
325
|
+
|
|
326
|
+
await fs.writeFile(envTemplatePath, lines.join('\n'), 'utf8');
|
|
327
|
+
logger.log(chalk.green('✓ Generated env.template'));
|
|
328
|
+
} catch (error) {
|
|
329
|
+
throw new Error(`Failed to generate env.template: ${error.message}`);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Generate deployment scripts (deploy.sh and deploy.ps1) from templates
|
|
335
|
+
* @async
|
|
336
|
+
* @function generateDeployScripts
|
|
337
|
+
* @param {string} appPath - Application directory path
|
|
338
|
+
* @param {string} systemKey - System key
|
|
339
|
+
* @param {string} systemFileName - System file name
|
|
340
|
+
* @param {string[]} datasourceFileNames - Array of datasource file names
|
|
341
|
+
* @returns {Promise<Object>} Object with script file paths
|
|
342
|
+
* @throws {Error} If generation fails
|
|
343
|
+
*/
|
|
344
|
+
async function generateDeployScripts(appPath, systemKey, systemFileName, datasourceFileNames) {
|
|
345
|
+
try {
|
|
346
|
+
const allJsonFiles = [systemFileName, ...datasourceFileNames];
|
|
347
|
+
|
|
348
|
+
// Load and compile deploy.sh template
|
|
349
|
+
const deployShTemplatePath = path.join(__dirname, '..', '..', 'templates', 'external-system', 'deploy.sh.hbs');
|
|
350
|
+
const deployShTemplateContent = await fs.readFile(deployShTemplatePath, 'utf8');
|
|
351
|
+
const deployShTemplate = Handlebars.compile(deployShTemplateContent);
|
|
352
|
+
|
|
353
|
+
// Generate deploy.sh
|
|
354
|
+
const deployShPath = path.join(appPath, 'deploy.sh');
|
|
355
|
+
const deployShContent = deployShTemplate({
|
|
356
|
+
systemKey,
|
|
357
|
+
allJsonFiles,
|
|
358
|
+
datasourceFileNames
|
|
359
|
+
});
|
|
360
|
+
await fs.writeFile(deployShPath, deployShContent, 'utf8');
|
|
361
|
+
await fs.chmod(deployShPath, 0o755); // Make executable
|
|
362
|
+
logger.log(chalk.green('✓ Generated deploy.sh'));
|
|
363
|
+
|
|
364
|
+
// Load and compile deploy.ps1 template
|
|
365
|
+
const deployPs1TemplatePath = path.join(__dirname, '..', '..', 'templates', 'external-system', 'deploy.ps1.hbs');
|
|
366
|
+
const deployPs1TemplateContent = await fs.readFile(deployPs1TemplatePath, 'utf8');
|
|
367
|
+
const deployPs1Template = Handlebars.compile(deployPs1TemplateContent);
|
|
368
|
+
|
|
369
|
+
// Generate deploy.ps1
|
|
370
|
+
const deployPs1Path = path.join(appPath, 'deploy.ps1');
|
|
371
|
+
const deployPs1Content = deployPs1Template({
|
|
372
|
+
systemKey,
|
|
373
|
+
allJsonFiles,
|
|
374
|
+
datasourceFileNames
|
|
375
|
+
});
|
|
376
|
+
await fs.writeFile(deployPs1Path, deployPs1Content, 'utf8');
|
|
377
|
+
logger.log(chalk.green('✓ Generated deploy.ps1'));
|
|
378
|
+
|
|
379
|
+
return {
|
|
380
|
+
deployShPath,
|
|
381
|
+
deployPs1Path
|
|
382
|
+
};
|
|
383
|
+
} catch (error) {
|
|
384
|
+
throw new Error(`Failed to generate deployment scripts: ${error.message}`);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Generate README.md with basic documentation
|
|
390
|
+
* @async
|
|
391
|
+
* @function generateReadme
|
|
392
|
+
* @param {string} appPath - Application directory path
|
|
393
|
+
* @param {string} appName - Application name
|
|
394
|
+
* @param {string} systemKey - System key
|
|
395
|
+
* @param {Object} systemConfig - System configuration
|
|
396
|
+
* @param {Object[]} datasourceConfigs - Array of datasource configurations
|
|
397
|
+
* @param {string} [aiGeneratedContent] - Optional AI-generated README content from dataplane
|
|
398
|
+
* @throws {Error} If generation fails
|
|
399
|
+
*/
|
|
400
|
+
async function generateReadme(appPath, appName, systemKey, systemConfig, datasourceConfigs, aiGeneratedContent) {
|
|
401
|
+
try {
|
|
402
|
+
const readmePath = path.join(appPath, 'README.md');
|
|
403
|
+
|
|
404
|
+
// Use AI-generated content if available, otherwise generate basic README
|
|
405
|
+
if (aiGeneratedContent) {
|
|
406
|
+
await fs.writeFile(readmePath, aiGeneratedContent, 'utf8');
|
|
407
|
+
logger.log(chalk.green('✓ Generated README.md (AI-generated from dataplane)'));
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const displayName = systemConfig.displayName || appName.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
|
|
412
|
+
const description = systemConfig.description || `External system integration for ${appName}`;
|
|
413
|
+
|
|
414
|
+
const lines = [
|
|
415
|
+
`# ${displayName}`,
|
|
416
|
+
'',
|
|
417
|
+
description,
|
|
418
|
+
'',
|
|
419
|
+
'## Overview',
|
|
420
|
+
'',
|
|
421
|
+
'This integration was created using the AI Fabrix wizard.',
|
|
422
|
+
'',
|
|
423
|
+
'## Files',
|
|
424
|
+
'',
|
|
425
|
+
`- \`${systemKey}-deploy.json\` - External system configuration`,
|
|
426
|
+
...datasourceConfigs.map((ds, index) => {
|
|
427
|
+
const entityType = ds.entityType || ds.entityKey || ds.key?.split('-').pop() || `datasource${index + 1}`;
|
|
428
|
+
return `- \`${systemKey}-deploy-${entityType}.json\` - Datasource configuration`;
|
|
429
|
+
}),
|
|
430
|
+
'- `variables.yaml` - Application variables and external integration configuration',
|
|
431
|
+
'- `env.template` - Environment variable template',
|
|
432
|
+
'- `application-schema.json` - Single deployment file',
|
|
433
|
+
'- `deploy.sh` - Bash deployment script',
|
|
434
|
+
'- `deploy.ps1` - PowerShell deployment script',
|
|
435
|
+
'',
|
|
436
|
+
'## Deployment',
|
|
437
|
+
'',
|
|
438
|
+
'### Using Deployment Scripts',
|
|
439
|
+
'',
|
|
440
|
+
'You can deploy using the provided scripts:',
|
|
441
|
+
'',
|
|
442
|
+
'**Bash (Linux/macOS):**',
|
|
443
|
+
'```bash',
|
|
444
|
+
'./deploy.sh',
|
|
445
|
+
'```',
|
|
446
|
+
'',
|
|
447
|
+
'**PowerShell (Windows):**',
|
|
448
|
+
'```powershell',
|
|
449
|
+
'.\\deploy.ps1',
|
|
450
|
+
'```',
|
|
451
|
+
'',
|
|
452
|
+
'The scripts support environment variables:',
|
|
453
|
+
'- `ENVIRONMENT` - Environment key (default: dev)',
|
|
454
|
+
'- `CONTROLLER` - Controller URL (default: http://localhost:3000)',
|
|
455
|
+
'- `RUN_TESTS` - Set to "true" to run integration tests after deployment',
|
|
456
|
+
'',
|
|
457
|
+
'**Example:**',
|
|
458
|
+
'```bash',
|
|
459
|
+
'ENVIRONMENT=prod CONTROLLER=https://controller.example.com ./deploy.sh',
|
|
460
|
+
'```',
|
|
461
|
+
'',
|
|
462
|
+
'### Using CLI Directly',
|
|
463
|
+
'',
|
|
464
|
+
'To deploy this external system:',
|
|
465
|
+
'',
|
|
466
|
+
'```bash',
|
|
467
|
+
`aifabrix deploy ${appName}`,
|
|
468
|
+
'```',
|
|
469
|
+
'',
|
|
470
|
+
'## Configuration',
|
|
471
|
+
'',
|
|
472
|
+
'Update the environment variables in `env.template` and set the values in your secrets store.',
|
|
473
|
+
'',
|
|
474
|
+
'## Documentation',
|
|
475
|
+
'',
|
|
476
|
+
'For more information, see the [External Systems Documentation](../../docs/external-systems.md).'
|
|
477
|
+
];
|
|
478
|
+
|
|
479
|
+
await fs.writeFile(readmePath, lines.join('\n'), 'utf8');
|
|
480
|
+
logger.log(chalk.green('✓ Generated README.md'));
|
|
481
|
+
} catch (error) {
|
|
482
|
+
throw new Error(`Failed to generate README.md: ${error.message}`);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
module.exports = {
|
|
487
|
+
generateWizardFiles,
|
|
488
|
+
generateDeployScripts
|
|
489
|
+
};
|
|
490
|
+
|
|
@@ -14,14 +14,14 @@ const { promisify } = require('util');
|
|
|
14
14
|
const path = require('path');
|
|
15
15
|
const fs = require('fs');
|
|
16
16
|
const handlebars = require('handlebars');
|
|
17
|
-
const secrets = require('
|
|
18
|
-
const config = require('
|
|
19
|
-
const devConfig = require('
|
|
20
|
-
const logger = require('
|
|
21
|
-
const containerUtils = require('
|
|
22
|
-
const dockerUtils = require('
|
|
23
|
-
const paths = require('
|
|
24
|
-
const statusHelpers = require('
|
|
17
|
+
const secrets = require('../core/secrets');
|
|
18
|
+
const config = require('../core/config');
|
|
19
|
+
const devConfig = require('../utils/dev-config');
|
|
20
|
+
const logger = require('../utils/logger');
|
|
21
|
+
const containerUtils = require('../utils/infra-containers');
|
|
22
|
+
const dockerUtils = require('../utils/docker');
|
|
23
|
+
const paths = require('../utils/paths');
|
|
24
|
+
const statusHelpers = require('../utils/infra-status');
|
|
25
25
|
|
|
26
26
|
// Register Handlebars helper for equality check
|
|
27
27
|
// Handles both strict equality and numeric string comparisons
|
|
@@ -238,7 +238,14 @@ async function copyPgAdminConfig(pgadminContainerName, serversJsonPath, pgpassPa
|
|
|
238
238
|
}
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
-
|
|
241
|
+
/**
|
|
242
|
+
* Prepares infrastructure environment
|
|
243
|
+
* @async
|
|
244
|
+
* @function prepareInfrastructureEnvironment
|
|
245
|
+
* @param {string|number|null} developerId - Developer ID
|
|
246
|
+
* @returns {Promise<Object>} Prepared environment configuration
|
|
247
|
+
*/
|
|
248
|
+
async function prepareInfrastructureEnvironment(developerId) {
|
|
242
249
|
await checkDockerAvailability();
|
|
243
250
|
const adminSecretsPath = await ensureAdminSecrets();
|
|
244
251
|
|
|
@@ -255,6 +262,38 @@ async function startInfra(developerId = null) {
|
|
|
255
262
|
// Prepare infrastructure directory
|
|
256
263
|
const { infraDir } = prepareInfraDirectory(devId, adminSecretsPath);
|
|
257
264
|
|
|
265
|
+
return { devId, idNum, ports, templatePath, infraDir, adminSecretsPath };
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Starts Docker services and configures pgAdmin
|
|
270
|
+
* @async
|
|
271
|
+
* @function startDockerServicesAndConfigure
|
|
272
|
+
* @param {string} composePath - Compose file path
|
|
273
|
+
* @param {string} devId - Developer ID
|
|
274
|
+
* @param {number} idNum - Developer ID number
|
|
275
|
+
* @param {string} adminSecretsPath - Admin secrets path
|
|
276
|
+
* @param {string} infraDir - Infrastructure directory
|
|
277
|
+
*/
|
|
278
|
+
async function startDockerServicesAndConfigure(composePath, devId, idNum, adminSecretsPath, infraDir) {
|
|
279
|
+
// Start Docker services
|
|
280
|
+
const projectName = getInfraProjectName(devId);
|
|
281
|
+
await startDockerServices(composePath, projectName, adminSecretsPath, infraDir);
|
|
282
|
+
|
|
283
|
+
// Copy pgAdmin4 config files
|
|
284
|
+
const pgadminContainerName = idNum === 0 ? 'aifabrix-pgadmin' : `aifabrix-dev${devId}-pgadmin`;
|
|
285
|
+
const serversJsonPath = path.join(infraDir, 'servers.json');
|
|
286
|
+
const pgpassPath = path.join(infraDir, 'pgpass');
|
|
287
|
+
await copyPgAdminConfig(pgadminContainerName, serversJsonPath, pgpassPath);
|
|
288
|
+
|
|
289
|
+
// Wait for services to be healthy
|
|
290
|
+
await waitForServices(devId);
|
|
291
|
+
logger.log('All services are healthy and ready');
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
async function startInfra(developerId = null) {
|
|
295
|
+
const { devId, idNum, ports, templatePath, infraDir, adminSecretsPath } = await prepareInfrastructureEnvironment(developerId);
|
|
296
|
+
|
|
258
297
|
// Register Handlebars helper
|
|
259
298
|
registerHandlebarsHelper();
|
|
260
299
|
|
|
@@ -262,19 +301,7 @@ async function startInfra(developerId = null) {
|
|
|
262
301
|
const composePath = generateComposeFile(templatePath, devId, idNum, ports, infraDir);
|
|
263
302
|
|
|
264
303
|
try {
|
|
265
|
-
|
|
266
|
-
const projectName = getInfraProjectName(devId);
|
|
267
|
-
await startDockerServices(composePath, projectName, adminSecretsPath, infraDir);
|
|
268
|
-
|
|
269
|
-
// Copy pgAdmin4 config files
|
|
270
|
-
const pgadminContainerName = idNum === 0 ? 'aifabrix-pgadmin' : `aifabrix-dev${devId}-pgadmin`;
|
|
271
|
-
const serversJsonPath = path.join(infraDir, 'servers.json');
|
|
272
|
-
const pgpassPath = path.join(infraDir, 'pgpass');
|
|
273
|
-
await copyPgAdminConfig(pgadminContainerName, serversJsonPath, pgpassPath);
|
|
274
|
-
|
|
275
|
-
// Wait for services to be healthy
|
|
276
|
-
await waitForServices(devId);
|
|
277
|
-
logger.log('All services are healthy and ready');
|
|
304
|
+
await startDockerServicesAndConfigure(composePath, devId, idNum, adminSecretsPath, infraDir);
|
|
278
305
|
} finally {
|
|
279
306
|
// Keep the compose file for stop commands
|
|
280
307
|
}
|