@aifabrix/builder 2.31.0 → 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} +123 -37
- 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 +34 -3
- package/scripts/install-local.js +210 -0
- 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
|
@@ -186,7 +186,8 @@ function detectFromRequiredFields(parsed) {
|
|
|
186
186
|
* @returns {string|null} Schema type or null if not detected
|
|
187
187
|
*/
|
|
188
188
|
function detectFromDatasourceFields(parsed) {
|
|
189
|
-
|
|
189
|
+
// Support both entityType (new) and entityKey (old) for backward compatibility during migration
|
|
190
|
+
if (parsed.systemKey && (parsed.entityType || parsed.entityKey) && parsed.fieldMappings) {
|
|
190
191
|
return 'external-datasource';
|
|
191
192
|
}
|
|
192
193
|
return null;
|
|
@@ -237,10 +238,17 @@ function detectFromFilename(filePath) {
|
|
|
237
238
|
* const type = detectSchemaType('./hubspot.json');
|
|
238
239
|
* // Returns: 'external-system'
|
|
239
240
|
*/
|
|
240
|
-
|
|
241
|
+
/**
|
|
242
|
+
* Reads and parses file content
|
|
243
|
+
* @function readAndParseFileContent
|
|
244
|
+
* @param {string} filePath - File path
|
|
245
|
+
* @param {string|undefined} content - Optional file content
|
|
246
|
+
* @returns {Object} Parsed JSON object
|
|
247
|
+
* @throws {Error} If file not found or invalid JSON
|
|
248
|
+
*/
|
|
249
|
+
function readAndParseFileContent(filePath, content) {
|
|
241
250
|
let fileContent = content;
|
|
242
251
|
|
|
243
|
-
// Read file if content not provided
|
|
244
252
|
if (!fileContent) {
|
|
245
253
|
if (!fs.existsSync(filePath)) {
|
|
246
254
|
throw new Error(`File not found: ${filePath}`);
|
|
@@ -248,35 +256,44 @@ function detectSchemaType(filePath, content) {
|
|
|
248
256
|
fileContent = fs.readFileSync(filePath, 'utf8');
|
|
249
257
|
}
|
|
250
258
|
|
|
251
|
-
// Try to parse JSON
|
|
252
|
-
let parsed;
|
|
253
259
|
try {
|
|
254
|
-
|
|
260
|
+
return JSON.parse(fileContent);
|
|
255
261
|
} catch (error) {
|
|
256
262
|
throw new Error(`Invalid JSON in file: ${error.message}`);
|
|
257
263
|
}
|
|
264
|
+
}
|
|
258
265
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
266
|
+
/**
|
|
267
|
+
* Tries detection methods in order
|
|
268
|
+
* @function tryDetectionMethods
|
|
269
|
+
* @param {Object} parsed - Parsed JSON object
|
|
270
|
+
* @param {string} filePath - File path
|
|
271
|
+
* @returns {string|null} Detected type or null
|
|
272
|
+
*/
|
|
273
|
+
function tryDetectionMethods(parsed, filePath) {
|
|
274
|
+
const detectionMethods = [
|
|
275
|
+
() => detectFromId(parsed),
|
|
276
|
+
() => detectFromTitle(parsed),
|
|
277
|
+
() => detectFromRequiredFields(parsed),
|
|
278
|
+
() => detectFromDatasourceFields(parsed),
|
|
279
|
+
() => detectFromApplicationFields(parsed),
|
|
280
|
+
() => detectFromFilename(filePath)
|
|
281
|
+
];
|
|
282
|
+
|
|
283
|
+
for (const method of detectionMethods) {
|
|
284
|
+
const result = method();
|
|
285
|
+
if (result) {
|
|
286
|
+
return result;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
274
289
|
|
|
275
|
-
|
|
276
|
-
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
277
292
|
|
|
278
|
-
|
|
279
|
-
|
|
293
|
+
function detectSchemaType(filePath, content) {
|
|
294
|
+
const parsed = readAndParseFileContent(filePath, content);
|
|
295
|
+
const detectedType = tryDetectionMethods(parsed, filePath);
|
|
296
|
+
return detectedType || 'application';
|
|
280
297
|
}
|
|
281
298
|
|
|
282
299
|
module.exports = {
|
|
@@ -28,56 +28,66 @@ const { detectAppType } = require('./paths');
|
|
|
28
28
|
* const basePath = await resolveSchemaBasePath('myapp');
|
|
29
29
|
* // Returns: '/path/to/builder/myapp/schemas'
|
|
30
30
|
*/
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Loads and validates variables.yaml
|
|
33
|
+
* @async
|
|
34
|
+
* @function loadAndValidateVariablesForSchema
|
|
35
|
+
* @param {string} appName - Application name
|
|
36
|
+
* @param {string} appPath - Application path
|
|
37
|
+
* @returns {Promise<Object>} Variables object
|
|
38
|
+
* @throws {Error} If file not found or invalid
|
|
39
|
+
*/
|
|
40
|
+
async function loadAndValidateVariablesForSchema(appName, appPath) {
|
|
38
41
|
const variablesPath = path.join(appPath, 'variables.yaml');
|
|
39
|
-
|
|
40
42
|
if (!fs.existsSync(variablesPath)) {
|
|
41
43
|
throw new Error(`variables.yaml not found: ${variablesPath}`);
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
const content = fs.readFileSync(variablesPath, 'utf8');
|
|
45
|
-
let variables;
|
|
46
|
-
|
|
47
47
|
try {
|
|
48
|
-
|
|
48
|
+
return yaml.load(content);
|
|
49
49
|
} catch (error) {
|
|
50
50
|
throw new Error(`Invalid YAML syntax in variables.yaml: ${error.message}`);
|
|
51
51
|
}
|
|
52
|
+
}
|
|
52
53
|
|
|
53
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Validates externalIntegration block
|
|
56
|
+
* @function validateExternalIntegrationBlock
|
|
57
|
+
* @param {Object} variables - Variables object
|
|
58
|
+
* @param {string} appName - Application name
|
|
59
|
+
* @returns {string} Schema base path
|
|
60
|
+
* @throws {Error} If block is missing or invalid
|
|
61
|
+
*/
|
|
62
|
+
function validateExternalIntegrationBlock(variables, appName) {
|
|
54
63
|
if (!variables.externalIntegration) {
|
|
55
64
|
throw new Error(`externalIntegration block not found in variables.yaml for app: ${appName}`);
|
|
56
65
|
}
|
|
57
|
-
|
|
58
66
|
if (!variables.externalIntegration.schemaBasePath) {
|
|
59
67
|
throw new Error(`schemaBasePath not found in externalIntegration block for app: ${appName}`);
|
|
60
68
|
}
|
|
69
|
+
return variables.externalIntegration.schemaBasePath;
|
|
70
|
+
}
|
|
61
71
|
|
|
62
|
-
|
|
72
|
+
/**
|
|
73
|
+
* Resolves and validates schema base path
|
|
74
|
+
* @function resolveAndValidateSchemaPath
|
|
75
|
+
* @param {string} schemaBasePath - Schema base path from config
|
|
76
|
+
* @param {string} variablesPath - Path to variables.yaml
|
|
77
|
+
* @returns {string} Resolved and validated path
|
|
78
|
+
* @throws {Error} If path is invalid
|
|
79
|
+
*/
|
|
80
|
+
function resolveAndValidateSchemaPath(schemaBasePath, variablesPath) {
|
|
63
81
|
const variablesDir = path.dirname(variablesPath);
|
|
82
|
+
let resolvedPath = path.isAbsolute(schemaBasePath)
|
|
83
|
+
? schemaBasePath
|
|
84
|
+
: path.resolve(variablesDir, schemaBasePath);
|
|
64
85
|
|
|
65
|
-
// Resolve path (absolute or relative to variables.yaml location)
|
|
66
|
-
let resolvedPath;
|
|
67
|
-
if (path.isAbsolute(schemaBasePath)) {
|
|
68
|
-
resolvedPath = schemaBasePath;
|
|
69
|
-
} else {
|
|
70
|
-
resolvedPath = path.resolve(variablesDir, schemaBasePath);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Normalize path
|
|
74
86
|
resolvedPath = path.normalize(resolvedPath);
|
|
75
87
|
|
|
76
|
-
// Validate path exists
|
|
77
88
|
if (!fs.existsSync(resolvedPath)) {
|
|
78
89
|
throw new Error(`Schema base path does not exist: ${resolvedPath}`);
|
|
79
90
|
}
|
|
80
|
-
|
|
81
91
|
if (!fs.statSync(resolvedPath).isDirectory()) {
|
|
82
92
|
throw new Error(`Schema base path is not a directory: ${resolvedPath}`);
|
|
83
93
|
}
|
|
@@ -85,6 +95,19 @@ async function resolveSchemaBasePath(appName) {
|
|
|
85
95
|
return resolvedPath;
|
|
86
96
|
}
|
|
87
97
|
|
|
98
|
+
async function resolveSchemaBasePath(appName) {
|
|
99
|
+
if (!appName || typeof appName !== 'string') {
|
|
100
|
+
throw new Error('App name is required and must be a string');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const { appPath } = await detectAppType(appName);
|
|
104
|
+
const variables = await loadAndValidateVariablesForSchema(appName, appPath);
|
|
105
|
+
const schemaBasePath = validateExternalIntegrationBlock(variables, appName);
|
|
106
|
+
const variablesPath = path.join(appPath, 'variables.yaml');
|
|
107
|
+
|
|
108
|
+
return resolveAndValidateSchemaPath(schemaBasePath, variablesPath);
|
|
109
|
+
}
|
|
110
|
+
|
|
88
111
|
/**
|
|
89
112
|
* Resolves all external system and datasource files from application configuration
|
|
90
113
|
* Returns array of file paths with metadata
|
|
@@ -102,13 +125,69 @@ async function resolveSchemaBasePath(appName) {
|
|
|
102
125
|
* // { path: '/path/to/hubspot-deal.json', type: 'datasource', fileName: 'hubspot-deal.json' }
|
|
103
126
|
* // ]
|
|
104
127
|
*/
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
128
|
+
/**
|
|
129
|
+
* Resolves a single external file
|
|
130
|
+
* @function resolveSingleExternalFile
|
|
131
|
+
* @param {string} schemaBasePath - Schema base path
|
|
132
|
+
* @param {string} fileName - File name
|
|
133
|
+
* @param {string} type - File type ('system' or 'datasource')
|
|
134
|
+
* @returns {Object} Resolved file object
|
|
135
|
+
* @throws {Error} If file not found
|
|
136
|
+
*/
|
|
137
|
+
function resolveSingleExternalFile(schemaBasePath, fileName, type) {
|
|
138
|
+
const filePath = path.join(schemaBasePath, fileName);
|
|
139
|
+
const normalizedPath = path.normalize(filePath);
|
|
140
|
+
|
|
141
|
+
if (!fs.existsSync(normalizedPath)) {
|
|
142
|
+
throw new Error(`External ${type} file not found: ${normalizedPath}`);
|
|
108
143
|
}
|
|
109
144
|
|
|
110
|
-
|
|
111
|
-
|
|
145
|
+
return {
|
|
146
|
+
path: normalizedPath,
|
|
147
|
+
type: type,
|
|
148
|
+
fileName: fileName
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Resolves system files
|
|
154
|
+
* @function resolveSystemFiles
|
|
155
|
+
* @param {string} schemaBasePath - Schema base path
|
|
156
|
+
* @param {string[]} systemFiles - Array of system file names
|
|
157
|
+
* @returns {Object[]} Array of resolved file objects
|
|
158
|
+
*/
|
|
159
|
+
function resolveSystemFiles(schemaBasePath, systemFiles) {
|
|
160
|
+
if (!systemFiles || !Array.isArray(systemFiles)) {
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return systemFiles.map(fileName => resolveSingleExternalFile(schemaBasePath, fileName, 'system'));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Resolves datasource files
|
|
169
|
+
* @function resolveDatasourceFiles
|
|
170
|
+
* @param {string} schemaBasePath - Schema base path
|
|
171
|
+
* @param {string[]} datasourceFiles - Array of datasource file names
|
|
172
|
+
* @returns {Object[]} Array of resolved file objects
|
|
173
|
+
*/
|
|
174
|
+
function resolveDatasourceFiles(schemaBasePath, datasourceFiles) {
|
|
175
|
+
if (!datasourceFiles || !Array.isArray(datasourceFiles)) {
|
|
176
|
+
return [];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return datasourceFiles.map(fileName => resolveSingleExternalFile(schemaBasePath, fileName, 'datasource'));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Loads and validates variables.yaml
|
|
184
|
+
* @async
|
|
185
|
+
* @function loadAndValidateVariables
|
|
186
|
+
* @param {string} appPath - Application path
|
|
187
|
+
* @returns {Promise<Object>} Variables object
|
|
188
|
+
* @throws {Error} If file not found or invalid
|
|
189
|
+
*/
|
|
190
|
+
async function loadAndValidateVariables(appPath) {
|
|
112
191
|
const variablesPath = path.join(appPath, 'variables.yaml');
|
|
113
192
|
|
|
114
193
|
if (!fs.existsSync(variablesPath)) {
|
|
@@ -116,60 +195,30 @@ async function resolveExternalFiles(appName) {
|
|
|
116
195
|
}
|
|
117
196
|
|
|
118
197
|
const content = fs.readFileSync(variablesPath, 'utf8');
|
|
119
|
-
let variables;
|
|
120
|
-
|
|
121
198
|
try {
|
|
122
|
-
|
|
199
|
+
return yaml.load(content);
|
|
123
200
|
} catch (error) {
|
|
124
201
|
throw new Error(`Invalid YAML syntax in variables.yaml: ${error.message}`);
|
|
125
202
|
}
|
|
203
|
+
}
|
|
126
204
|
|
|
127
|
-
|
|
128
|
-
if (!
|
|
129
|
-
|
|
205
|
+
async function resolveExternalFiles(appName) {
|
|
206
|
+
if (!appName || typeof appName !== 'string') {
|
|
207
|
+
throw new Error('App name is required and must be a string');
|
|
130
208
|
}
|
|
131
209
|
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
const resolvedFiles = [];
|
|
135
|
-
|
|
136
|
-
// Resolve systems files
|
|
137
|
-
if (variables.externalIntegration.systems && Array.isArray(variables.externalIntegration.systems)) {
|
|
138
|
-
for (const systemFile of variables.externalIntegration.systems) {
|
|
139
|
-
const systemPath = path.join(schemaBasePath, systemFile);
|
|
140
|
-
const normalizedPath = path.normalize(systemPath);
|
|
141
|
-
|
|
142
|
-
if (!fs.existsSync(normalizedPath)) {
|
|
143
|
-
throw new Error(`External system file not found: ${normalizedPath}`);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
resolvedFiles.push({
|
|
147
|
-
path: normalizedPath,
|
|
148
|
-
type: 'system',
|
|
149
|
-
fileName: systemFile
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
}
|
|
210
|
+
const { appPath } = await detectAppType(appName);
|
|
211
|
+
const variables = await loadAndValidateVariables(appPath);
|
|
153
212
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
for (const datasourceFile of variables.externalIntegration.dataSources) {
|
|
157
|
-
const datasourcePath = path.join(schemaBasePath, datasourceFile);
|
|
158
|
-
const normalizedPath = path.normalize(datasourcePath);
|
|
159
|
-
|
|
160
|
-
if (!fs.existsSync(normalizedPath)) {
|
|
161
|
-
throw new Error(`External datasource file not found: ${normalizedPath}`);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
resolvedFiles.push({
|
|
165
|
-
path: normalizedPath,
|
|
166
|
-
type: 'datasource',
|
|
167
|
-
fileName: datasourceFile
|
|
168
|
-
});
|
|
169
|
-
}
|
|
213
|
+
if (!variables.externalIntegration) {
|
|
214
|
+
return [];
|
|
170
215
|
}
|
|
171
216
|
|
|
172
|
-
|
|
217
|
+
const schemaBasePath = await resolveSchemaBasePath(appName);
|
|
218
|
+
const systemFiles = resolveSystemFiles(schemaBasePath, variables.externalIntegration.systems);
|
|
219
|
+
const datasourceFiles = resolveDatasourceFiles(schemaBasePath, variables.externalIntegration.dataSources);
|
|
220
|
+
|
|
221
|
+
return [...systemFiles, ...datasourceFiles];
|
|
173
222
|
}
|
|
174
223
|
|
|
175
224
|
module.exports = {
|
|
@@ -131,48 +131,85 @@ function encryptSecret(value, key) {
|
|
|
131
131
|
* const decrypted = decryptSecret('secure://<iv>:<ciphertext>:<authTag>', 'a1b2c3...');
|
|
132
132
|
* // Returns: 'my-secret'
|
|
133
133
|
*/
|
|
134
|
-
|
|
134
|
+
/**
|
|
135
|
+
* Validates encrypted value format
|
|
136
|
+
* @function validateEncryptedValueFormat
|
|
137
|
+
* @param {string} encryptedValue - Encrypted value
|
|
138
|
+
* @throws {Error} If format is invalid
|
|
139
|
+
*/
|
|
140
|
+
function validateEncryptedValueFormat(encryptedValue) {
|
|
135
141
|
if (!encryptedValue || typeof encryptedValue !== 'string') {
|
|
136
142
|
throw new Error('Encrypted value is required and must be a string');
|
|
137
143
|
}
|
|
138
|
-
|
|
139
144
|
if (!encryptedValue.startsWith('secure://')) {
|
|
140
145
|
throw new Error('Encrypted value must start with secure:// prefix');
|
|
141
146
|
}
|
|
147
|
+
}
|
|
142
148
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
149
|
+
/**
|
|
150
|
+
* Parses encrypted value parts
|
|
151
|
+
* @function parseEncryptedValueParts
|
|
152
|
+
* @param {string} encryptedValue - Encrypted value
|
|
153
|
+
* @returns {Object} Object with ivBase64, ciphertext, and authTagBase64
|
|
154
|
+
* @throws {Error} If format is invalid
|
|
155
|
+
*/
|
|
156
|
+
function parseEncryptedValueParts(encryptedValue) {
|
|
146
157
|
const parts = encryptedValue.substring(9).split(':');
|
|
147
158
|
if (parts.length !== 3) {
|
|
148
159
|
throw new Error('Invalid encrypted value format. Expected: secure://<iv>:<ciphertext>:<authTag>');
|
|
149
160
|
}
|
|
161
|
+
return {
|
|
162
|
+
ivBase64: parts[0],
|
|
163
|
+
ciphertext: parts[1],
|
|
164
|
+
authTagBase64: parts[2]
|
|
165
|
+
};
|
|
166
|
+
}
|
|
150
167
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
168
|
+
/**
|
|
169
|
+
* Validates IV and auth tag lengths
|
|
170
|
+
* @function validateDecryptionComponents
|
|
171
|
+
* @param {Buffer} iv - Initialization vector
|
|
172
|
+
* @param {Buffer} authTag - Authentication tag
|
|
173
|
+
* @throws {Error} If lengths are invalid
|
|
174
|
+
*/
|
|
175
|
+
function validateDecryptionComponents(iv, authTag) {
|
|
176
|
+
if (iv.length !== IV_LENGTH) {
|
|
177
|
+
throw new Error(`Invalid IV length: expected ${IV_LENGTH} bytes, got ${iv.length}`);
|
|
178
|
+
}
|
|
179
|
+
if (authTag.length !== AUTH_TAG_LENGTH) {
|
|
180
|
+
throw new Error(`Invalid auth tag length: expected ${AUTH_TAG_LENGTH} bytes, got ${authTag.length}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
157
183
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
184
|
+
/**
|
|
185
|
+
* Performs decryption
|
|
186
|
+
* @function performDecryption
|
|
187
|
+
* @param {Buffer} keyBuffer - Key buffer
|
|
188
|
+
* @param {Buffer} iv - Initialization vector
|
|
189
|
+
* @param {string} ciphertext - Ciphertext
|
|
190
|
+
* @param {Buffer} authTag - Authentication tag
|
|
191
|
+
* @returns {string} Decrypted plaintext
|
|
192
|
+
*/
|
|
193
|
+
function performDecryption(keyBuffer, iv, ciphertext, authTag) {
|
|
194
|
+
const decipher = crypto.createDecipheriv(ALGORITHM, keyBuffer, iv);
|
|
195
|
+
decipher.setAuthTag(authTag);
|
|
196
|
+
let plaintext = decipher.update(ciphertext, 'base64', 'utf8');
|
|
197
|
+
plaintext += decipher.final('utf8');
|
|
198
|
+
return plaintext;
|
|
199
|
+
}
|
|
162
200
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}
|
|
201
|
+
function decryptSecret(encryptedValue, key) {
|
|
202
|
+
validateEncryptedValueFormat(encryptedValue);
|
|
166
203
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
decipher.setAuthTag(authTag);
|
|
204
|
+
const keyBuffer = normalizeKey(key);
|
|
205
|
+
const { ivBase64, ciphertext, authTagBase64 } = parseEncryptedValueParts(encryptedValue);
|
|
170
206
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
207
|
+
try {
|
|
208
|
+
const iv = Buffer.from(ivBase64, 'base64');
|
|
209
|
+
const authTag = Buffer.from(authTagBase64, 'base64');
|
|
174
210
|
|
|
175
|
-
|
|
211
|
+
validateDecryptionComponents(iv, authTag);
|
|
212
|
+
return performDecryption(keyBuffer, iv, ciphertext, authTag);
|
|
176
213
|
} catch (error) {
|
|
177
214
|
// Don't expose sensitive details in error messages
|
|
178
215
|
if (error.message.includes('Unsupported state') || error.message.includes('bad decrypt')) {
|