@aifabrix/builder 2.10.1 ā 2.11.0
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/integration/hubspot/README.md +2 -2
- package/integration/hubspot/hubspot-deploy.json +12 -4
- package/lib/app-register.js +70 -403
- package/lib/utils/api-error-handler.js +11 -453
- package/lib/utils/app-register-api.js +71 -0
- package/lib/utils/app-register-auth.js +72 -0
- package/lib/utils/app-register-config.js +205 -0
- package/lib/utils/app-register-display.js +69 -0
- package/lib/utils/app-register-validator.js +143 -0
- package/lib/utils/device-code.js +1 -1
- package/lib/utils/error-formatters/error-parser.js +150 -0
- package/lib/utils/error-formatters/http-status-errors.js +189 -0
- package/lib/utils/error-formatters/network-errors.js +46 -0
- package/lib/utils/error-formatters/permission-errors.js +94 -0
- package/lib/utils/error-formatters/validation-errors.js +133 -0
- package/package.json +1 -1
- package/templates/applications/README.md.hbs +1 -1
|
@@ -48,13 +48,13 @@ aifabrix validate hubspot
|
|
|
48
48
|
|
|
49
49
|
```bash
|
|
50
50
|
# Login to controller
|
|
51
|
-
aifabrix login --controller
|
|
51
|
+
aifabrix login --controller http://localhost:3100 --method device --environment dev
|
|
52
52
|
|
|
53
53
|
# Register application
|
|
54
54
|
aifabrix app register hubspot --environment dev
|
|
55
55
|
|
|
56
56
|
# Deploy entire system
|
|
57
|
-
aifabrix deploy hubspot --controller
|
|
57
|
+
aifabrix deploy hubspot --controller http://localhost:3100 --environment dev
|
|
58
58
|
|
|
59
59
|
# Or deploy individual datasources for testing
|
|
60
60
|
aifabrix datasource deploy hubspot-company --environment dev --file integration/hubspot/hubspot-deploy-company.json
|
|
@@ -58,7 +58,11 @@
|
|
|
58
58
|
"field": "select",
|
|
59
59
|
"label": "HubSpot API Version",
|
|
60
60
|
"placeholder": "Select API version",
|
|
61
|
-
"options": [
|
|
61
|
+
"options": [
|
|
62
|
+
"v1",
|
|
63
|
+
"v2",
|
|
64
|
+
"v3"
|
|
65
|
+
],
|
|
62
66
|
"validation": {
|
|
63
67
|
"required": false
|
|
64
68
|
}
|
|
@@ -86,6 +90,10 @@
|
|
|
86
90
|
"documentKey": "hubspot-v3",
|
|
87
91
|
"autoDiscoverEntities": false
|
|
88
92
|
},
|
|
89
|
-
"tags": [
|
|
90
|
-
|
|
91
|
-
|
|
93
|
+
"tags": [
|
|
94
|
+
"crm",
|
|
95
|
+
"sales",
|
|
96
|
+
"marketing",
|
|
97
|
+
"hubspot"
|
|
98
|
+
]
|
|
99
|
+
}
|
package/lib/app-register.js
CHANGED
|
@@ -8,376 +8,98 @@
|
|
|
8
8
|
* @version 2.0.0
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
const fs = require('fs').promises;
|
|
12
|
-
const path = require('path');
|
|
13
11
|
const chalk = require('chalk');
|
|
14
|
-
const yaml = require('js-yaml');
|
|
15
|
-
const { getConfig } = require('./config');
|
|
16
|
-
const { authenticatedApiCall } = require('./utils/api');
|
|
17
|
-
const { formatApiError } = require('./utils/api-error-handler');
|
|
18
12
|
const logger = require('./utils/logger');
|
|
19
13
|
const { saveLocalSecret, isLocalhost } = require('./utils/local-secrets');
|
|
20
14
|
const { updateEnvTemplate } = require('./utils/env-template');
|
|
21
|
-
const { getOrRefreshDeviceToken } = require('./utils/token-manager');
|
|
22
|
-
const { detectAppType } = require('./utils/paths');
|
|
23
15
|
const { generateEnvFile } = require('./secrets');
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Extract valid enum values from application schema
|
|
35
|
-
const validTypes = applicationSchema.properties.type.enum || [];
|
|
36
|
-
const validRegistryModes = applicationSchema.properties.registryMode.enum || [];
|
|
37
|
-
const portConstraints = {
|
|
38
|
-
minimum: applicationSchema.properties.port?.minimum || 1,
|
|
39
|
-
maximum: applicationSchema.properties.port?.maximum || 65535
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Validation schema for application registration
|
|
44
|
-
* Validates according to application-schema.json
|
|
45
|
-
*/
|
|
46
|
-
const registerApplicationSchema = {
|
|
47
|
-
environmentId: (val) => {
|
|
48
|
-
if (!val || val.length < 1) {
|
|
49
|
-
throw new Error('Invalid environment ID format');
|
|
50
|
-
}
|
|
51
|
-
return val;
|
|
52
|
-
},
|
|
53
|
-
key: (val) => {
|
|
54
|
-
if (!val || val.length < 1) {
|
|
55
|
-
throw new Error('Application key is required');
|
|
56
|
-
}
|
|
57
|
-
const keyPattern = applicationSchema.properties.key.pattern;
|
|
58
|
-
const keyMaxLength = applicationSchema.properties.key.maxLength || 50;
|
|
59
|
-
if (val.length > keyMaxLength) {
|
|
60
|
-
throw new Error(`Application key must be at most ${keyMaxLength} characters`);
|
|
61
|
-
}
|
|
62
|
-
if (keyPattern && !new RegExp(keyPattern).test(val)) {
|
|
63
|
-
throw new Error('Application key must contain only lowercase letters, numbers, and hyphens');
|
|
64
|
-
}
|
|
65
|
-
return val;
|
|
66
|
-
},
|
|
67
|
-
displayName: (val) => {
|
|
68
|
-
if (!val || val.length < 1) {
|
|
69
|
-
throw new Error('Display name is required');
|
|
70
|
-
}
|
|
71
|
-
const displayNameMaxLength = applicationSchema.properties.displayName.maxLength || 100;
|
|
72
|
-
if (val.length > displayNameMaxLength) {
|
|
73
|
-
throw new Error(`Display name must be at most ${displayNameMaxLength} characters`);
|
|
74
|
-
}
|
|
75
|
-
return val;
|
|
76
|
-
},
|
|
77
|
-
description: (val) => val || undefined,
|
|
78
|
-
configuration: (val) => {
|
|
79
|
-
if (!val || !val.type || !validTypes.includes(val.type)) {
|
|
80
|
-
throw new Error(`Configuration type must be one of: ${validTypes.join(', ')}`);
|
|
81
|
-
}
|
|
82
|
-
if (!val.registryMode || !validRegistryModes.includes(val.registryMode)) {
|
|
83
|
-
throw new Error(`Registry mode must be one of: ${validRegistryModes.join(', ')}`);
|
|
84
|
-
}
|
|
85
|
-
// Port validation: skip for external type (external systems don't need ports)
|
|
86
|
-
// For other types, port is required and must be valid
|
|
87
|
-
if (val.type !== 'external') {
|
|
88
|
-
if (val.port === undefined || val.port === null) {
|
|
89
|
-
throw new Error('Port is required for non-external application types');
|
|
90
|
-
}
|
|
91
|
-
if (!Number.isInteger(val.port) || val.port < portConstraints.minimum || val.port > portConstraints.maximum) {
|
|
92
|
-
throw new Error(`Port must be an integer between ${portConstraints.minimum} and ${portConstraints.maximum}`);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
return val;
|
|
96
|
-
}
|
|
97
|
-
};
|
|
16
|
+
const { registerApplicationSchema, validateAppRegistrationData } = require('./utils/app-register-validator');
|
|
17
|
+
const {
|
|
18
|
+
loadVariablesYaml,
|
|
19
|
+
createMinimalAppIfNeeded,
|
|
20
|
+
extractAppConfiguration
|
|
21
|
+
} = require('./utils/app-register-config');
|
|
22
|
+
const { checkAuthentication } = require('./utils/app-register-auth');
|
|
23
|
+
const { callRegisterApi } = require('./utils/app-register-api');
|
|
24
|
+
const { displayRegistrationResults, getEnvironmentPrefix } = require('./utils/app-register-display');
|
|
98
25
|
|
|
99
26
|
/**
|
|
100
|
-
*
|
|
101
|
-
* @
|
|
102
|
-
* @param {string} appKey - Application key
|
|
103
|
-
* @returns {Promise<{variables: Object, created: boolean}>} Variables and creation flag
|
|
104
|
-
*/
|
|
105
|
-
async function loadVariablesYaml(appKey) {
|
|
106
|
-
// Detect app type and get correct path (integration or builder)
|
|
107
|
-
const { appPath } = await detectAppType(appKey);
|
|
108
|
-
const variablesPath = path.join(appPath, 'variables.yaml');
|
|
109
|
-
|
|
110
|
-
try {
|
|
111
|
-
const variablesContent = await fs.readFile(variablesPath, 'utf-8');
|
|
112
|
-
return { variables: yaml.load(variablesContent), created: false };
|
|
113
|
-
} catch (error) {
|
|
114
|
-
if (error.code === 'ENOENT') {
|
|
115
|
-
logger.log(chalk.yellow(`ā ļø variables.yaml not found for ${appKey}`));
|
|
116
|
-
logger.log(chalk.yellow('š Creating minimal configuration...\n'));
|
|
117
|
-
return { variables: null, created: true };
|
|
118
|
-
}
|
|
119
|
-
throw new Error(`Failed to read variables.yaml: ${error.message}`);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Create minimal application configuration if needed
|
|
125
|
-
* @async
|
|
126
|
-
* @param {string} appKey - Application key
|
|
27
|
+
* Build registration data payload from app configuration
|
|
28
|
+
* @param {Object} appConfig - Application configuration
|
|
127
29
|
* @param {Object} options - Registration options
|
|
128
|
-
* @returns {
|
|
30
|
+
* @returns {Object} Registration data payload
|
|
129
31
|
*/
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
await createApp(appKey, {
|
|
136
|
-
port: options.port,
|
|
137
|
-
language: 'typescript',
|
|
138
|
-
database: false,
|
|
139
|
-
redis: false,
|
|
140
|
-
storage: false,
|
|
141
|
-
authentication: false
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
// Detect app type and get correct path (integration or builder)
|
|
145
|
-
const { appPath } = await detectAppType(appKey);
|
|
146
|
-
const variablesPath = path.join(appPath, 'variables.yaml');
|
|
147
|
-
const variablesContent = await fs.readFile(variablesPath, 'utf-8');
|
|
148
|
-
return yaml.load(variablesContent);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Extract application configuration from variables.yaml
|
|
153
|
-
* @param {Object} variables - Variables from YAML file
|
|
154
|
-
* @param {string} appKey - Application key
|
|
155
|
-
* @param {Object} options - Registration options
|
|
156
|
-
* @returns {Object} Extracted configuration
|
|
157
|
-
*/
|
|
158
|
-
function extractAppConfiguration(variables, appKey, options) {
|
|
159
|
-
const appKeyFromFile = variables.app?.key || appKey;
|
|
160
|
-
const displayName = variables.app?.name || options.name || appKey;
|
|
161
|
-
const description = variables.app?.description || '';
|
|
162
|
-
|
|
163
|
-
// Handle external type
|
|
164
|
-
if (variables.app?.type === 'external') {
|
|
165
|
-
return {
|
|
166
|
-
appKey: appKeyFromFile,
|
|
167
|
-
displayName,
|
|
168
|
-
description,
|
|
169
|
-
appType: 'external',
|
|
170
|
-
registryMode: 'external',
|
|
171
|
-
port: null, // External systems don't need ports
|
|
172
|
-
language: null // External systems don't need language
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const appType = variables.build?.language === 'typescript' ? 'webapp' : 'service';
|
|
177
|
-
const registryMode = 'external';
|
|
178
|
-
const port = variables.build?.port || options.port || 3000;
|
|
179
|
-
const language = variables.build?.language || 'typescript';
|
|
180
|
-
|
|
181
|
-
return {
|
|
182
|
-
appKey: appKeyFromFile,
|
|
183
|
-
displayName,
|
|
184
|
-
description,
|
|
185
|
-
appType,
|
|
186
|
-
registryMode,
|
|
187
|
-
port,
|
|
188
|
-
language
|
|
32
|
+
function buildRegistrationData(appConfig, options) {
|
|
33
|
+
const registrationData = {
|
|
34
|
+
key: appConfig.appKey,
|
|
35
|
+
displayName: appConfig.displayName,
|
|
36
|
+
type: appConfig.appType
|
|
189
37
|
};
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Validate application registration data
|
|
194
|
-
* @async
|
|
195
|
-
* @param {Object} config - Application configuration
|
|
196
|
-
* @param {string} originalAppKey - Original app key for error messages
|
|
197
|
-
* @throws {Error} If validation fails
|
|
198
|
-
*/
|
|
199
|
-
async function validateAppRegistrationData(config, originalAppKey) {
|
|
200
|
-
const missingFields = [];
|
|
201
|
-
if (!config.appKey) missingFields.push('app.key');
|
|
202
|
-
if (!config.displayName) missingFields.push('app.name');
|
|
203
|
-
|
|
204
|
-
if (missingFields.length > 0) {
|
|
205
|
-
logger.error(chalk.red('ā Missing required fields in variables.yaml:'));
|
|
206
|
-
missingFields.forEach(field => logger.error(chalk.red(` - ${field}`)));
|
|
207
|
-
// Detect app type to show correct path
|
|
208
|
-
const { appPath } = await detectAppType(originalAppKey);
|
|
209
|
-
const relativePath = path.relative(process.cwd(), appPath);
|
|
210
|
-
logger.error(chalk.red(`\n Please update ${relativePath}/variables.yaml and try again.`));
|
|
211
|
-
process.exit(1);
|
|
212
|
-
}
|
|
213
38
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
registerApplicationSchema.configuration({
|
|
218
|
-
type: config.appType,
|
|
219
|
-
registryMode: config.registryMode,
|
|
220
|
-
port: config.port
|
|
221
|
-
});
|
|
222
|
-
} catch (error) {
|
|
223
|
-
logger.error(chalk.red(`ā Invalid configuration: ${error.message}`));
|
|
224
|
-
process.exit(1);
|
|
39
|
+
// Add optional fields only if they have values
|
|
40
|
+
if (appConfig.description || options.description) {
|
|
41
|
+
registrationData.description = appConfig.description || options.description;
|
|
225
42
|
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Check if user is authenticated and get token
|
|
230
|
-
* @async
|
|
231
|
-
* @param {string} [controllerUrl] - Optional controller URL from variables.yaml
|
|
232
|
-
* @param {string} [environment] - Optional environment key
|
|
233
|
-
* @returns {Promise<{apiUrl: string, token: string}>} Configuration with API URL and token
|
|
234
|
-
*/
|
|
235
|
-
async function checkAuthentication(controllerUrl, environment) {
|
|
236
|
-
const config = await getConfig();
|
|
237
|
-
|
|
238
|
-
// Try to get controller URL from parameter, config, or device tokens
|
|
239
|
-
let finalControllerUrl = controllerUrl;
|
|
240
|
-
let token = null;
|
|
241
43
|
|
|
242
|
-
//
|
|
243
|
-
if (
|
|
244
|
-
|
|
245
|
-
if (
|
|
246
|
-
|
|
247
|
-
finalControllerUrl = deviceToken.controller;
|
|
44
|
+
// Handle external type vs non-external types differently
|
|
45
|
+
if (appConfig.appType === 'external') {
|
|
46
|
+
// For external type: include externalIntegration, exclude registryMode/port/image
|
|
47
|
+
if (appConfig.externalIntegration) {
|
|
48
|
+
registrationData.externalIntegration = appConfig.externalIntegration;
|
|
248
49
|
}
|
|
249
|
-
}
|
|
50
|
+
} else {
|
|
51
|
+
// For non-external types: include registryMode, port, image
|
|
52
|
+
registrationData.registryMode = appConfig.registryMode;
|
|
250
53
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
if (deviceUrls.length > 0) {
|
|
255
|
-
// Use first available device token
|
|
256
|
-
finalControllerUrl = deviceUrls[0];
|
|
257
|
-
const deviceToken = await getOrRefreshDeviceToken(finalControllerUrl);
|
|
258
|
-
if (deviceToken && deviceToken.token) {
|
|
259
|
-
token = deviceToken.token;
|
|
260
|
-
finalControllerUrl = deviceToken.controller;
|
|
261
|
-
}
|
|
54
|
+
// Port is required for non-external types
|
|
55
|
+
if (appConfig.port) {
|
|
56
|
+
registrationData.port = appConfig.port;
|
|
262
57
|
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// If still no token, check for client token (requires environment and app)
|
|
266
|
-
if (!token && environment) {
|
|
267
|
-
// For app register, we don't have an app yet, so client tokens won't work
|
|
268
|
-
// This is expected - device tokens should be used for registration
|
|
269
|
-
}
|
|
270
58
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
59
|
+
// Image is required for non-external types
|
|
60
|
+
if (appConfig.image) {
|
|
61
|
+
registrationData.image = appConfig.image;
|
|
62
|
+
}
|
|
275
63
|
}
|
|
276
64
|
|
|
277
|
-
return
|
|
278
|
-
apiUrl: finalControllerUrl,
|
|
279
|
-
token: token
|
|
280
|
-
};
|
|
65
|
+
return registrationData;
|
|
281
66
|
}
|
|
282
67
|
|
|
283
68
|
/**
|
|
284
|
-
*
|
|
69
|
+
* Save credentials to local secrets if localhost
|
|
285
70
|
* @async
|
|
71
|
+
* @param {Object} responseData - Registration response data
|
|
286
72
|
* @param {string} apiUrl - API URL
|
|
287
|
-
* @param {string} token - Authentication token
|
|
288
|
-
* @param {string} environment - Environment ID
|
|
289
|
-
* @param {Object} registrationData - Registration data
|
|
290
|
-
* @returns {Promise<Object>} API response
|
|
291
73
|
*/
|
|
292
|
-
async function
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
{
|
|
296
|
-
method: 'POST',
|
|
297
|
-
body: JSON.stringify(registrationData)
|
|
298
|
-
},
|
|
299
|
-
token
|
|
300
|
-
);
|
|
301
|
-
|
|
302
|
-
if (!response.success) {
|
|
303
|
-
const formattedError = response.formattedError || formatApiError(response);
|
|
304
|
-
logger.error(formattedError);
|
|
305
|
-
process.exit(1);
|
|
74
|
+
async function saveLocalCredentials(responseData, apiUrl) {
|
|
75
|
+
if (!isLocalhost(apiUrl)) {
|
|
76
|
+
return;
|
|
306
77
|
}
|
|
307
78
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
// 1. Direct format: { application: {...}, credentials: {...} }
|
|
312
|
-
// 2. Wrapped format: { success: true, data: { application: {...}, credentials: {...} } }
|
|
313
|
-
const apiResponse = response.data;
|
|
314
|
-
if (apiResponse && apiResponse.data && apiResponse.data.application) {
|
|
315
|
-
// Wrapped format: use apiResponse.data
|
|
316
|
-
return apiResponse.data;
|
|
317
|
-
} else if (apiResponse && apiResponse.application) {
|
|
318
|
-
// Direct format: use apiResponse directly
|
|
319
|
-
return apiResponse;
|
|
320
|
-
}
|
|
321
|
-
// Fallback: return apiResponse as-is (shouldn't happen, but handle gracefully)
|
|
322
|
-
logger.error(chalk.red('ā Invalid response: missing application data'));
|
|
323
|
-
logger.error(chalk.gray('\nFull response for debugging:'));
|
|
324
|
-
logger.error(chalk.gray(JSON.stringify(response, null, 2)));
|
|
325
|
-
process.exit(1);
|
|
326
|
-
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
/**
|
|
330
|
-
* Get environment prefix for GitHub Secrets
|
|
331
|
-
* @param {string} environment - Environment key (e.g., 'dev', 'tst', 'pro', 'miso')
|
|
332
|
-
* @returns {string} Uppercase prefix (e.g., 'DEV', 'TST', 'PRO', 'MISO')
|
|
333
|
-
*/
|
|
334
|
-
function getEnvironmentPrefix(environment) {
|
|
335
|
-
if (!environment) {
|
|
336
|
-
return 'DEV';
|
|
337
|
-
}
|
|
338
|
-
// Convert to uppercase and handle common variations
|
|
339
|
-
const env = environment.toLowerCase();
|
|
340
|
-
if (env === 'dev' || env === 'development') {
|
|
341
|
-
return 'DEV';
|
|
342
|
-
}
|
|
343
|
-
if (env === 'tst' || env === 'test' || env === 'staging') {
|
|
344
|
-
return 'TST';
|
|
345
|
-
}
|
|
346
|
-
if (env === 'pro' || env === 'prod' || env === 'production') {
|
|
347
|
-
return 'PRO';
|
|
348
|
-
}
|
|
349
|
-
// For other environments (e.g., 'miso'), uppercase the entire string
|
|
350
|
-
// Use full string if 4 characters or less, otherwise use first 4 characters
|
|
351
|
-
const upper = environment.toUpperCase();
|
|
352
|
-
return upper.length <= 4 ? upper : upper.substring(0, 4);
|
|
353
|
-
}
|
|
79
|
+
const registeredAppKey = responseData.application.key;
|
|
80
|
+
const clientIdKey = `${registeredAppKey}-client-idKeyVault`;
|
|
81
|
+
const clientSecretKey = `${registeredAppKey}-client-secretKeyVault`;
|
|
354
82
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
* @param {string} apiUrl - API URL
|
|
359
|
-
* @param {string} environment - Environment key
|
|
360
|
-
*/
|
|
361
|
-
function displayRegistrationResults(data, apiUrl, environment) {
|
|
362
|
-
logger.log(chalk.green('ā
Application registered successfully!\n'));
|
|
363
|
-
logger.log(chalk.bold('š Application Details:'));
|
|
364
|
-
logger.log(` ID: ${data.application.id}`);
|
|
365
|
-
logger.log(` Key: ${data.application.key}`);
|
|
366
|
-
logger.log(` Display Name: ${data.application.displayName}\n`);
|
|
83
|
+
try {
|
|
84
|
+
await saveLocalSecret(clientIdKey, responseData.credentials.clientId);
|
|
85
|
+
await saveLocalSecret(clientSecretKey, responseData.credentials.clientSecret);
|
|
367
86
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
logger.log(chalk.yellow(` Client Secret: ${data.credentials.clientSecret}\n`));
|
|
87
|
+
// Update env.template
|
|
88
|
+
await updateEnvTemplate(registeredAppKey, clientIdKey, clientSecretKey, apiUrl);
|
|
371
89
|
|
|
372
|
-
|
|
90
|
+
// Regenerate .env file with updated credentials
|
|
91
|
+
try {
|
|
92
|
+
await generateEnvFile(registeredAppKey, null, 'local');
|
|
93
|
+
logger.log(chalk.green('ā .env file updated with new credentials'));
|
|
94
|
+
} catch (error) {
|
|
95
|
+
logger.warn(chalk.yellow(`ā ļø Could not regenerate .env file: ${error.message}`));
|
|
96
|
+
}
|
|
373
97
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
logger.log(chalk.cyan(` ${envPrefix}_MISO_CLIENTID = ${data.credentials.clientId}`));
|
|
380
|
-
logger.log(chalk.cyan(` ${envPrefix}_MISO_CLIENTSECRET = ${data.credentials.clientSecret}\n`));
|
|
98
|
+
logger.log(chalk.green('\nā Credentials saved to ~/.aifabrix/secrets.local.yaml'));
|
|
99
|
+
logger.log(chalk.green('ā env.template updated with MISO_CLIENTID, MISO_CLIENTSECRET, and MISO_CONTROLLER_URL\n'));
|
|
100
|
+
} catch (error) {
|
|
101
|
+
logger.warn(chalk.yellow(`ā ļø Could not save credentials locally: ${error.message}`));
|
|
102
|
+
}
|
|
381
103
|
}
|
|
382
104
|
|
|
383
105
|
/**
|
|
@@ -396,49 +118,21 @@ async function registerApplication(appKey, options) {
|
|
|
396
118
|
|
|
397
119
|
// Load variables.yaml
|
|
398
120
|
const { variables, created } = await loadVariablesYaml(appKey);
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
if (created) {
|
|
403
|
-
finalVariables = await createMinimalAppIfNeeded(appKey, options);
|
|
404
|
-
}
|
|
121
|
+
const finalVariables = created
|
|
122
|
+
? await createMinimalAppIfNeeded(appKey, options)
|
|
123
|
+
: variables;
|
|
405
124
|
|
|
406
|
-
// Extract configuration
|
|
407
|
-
const appConfig = extractAppConfiguration(finalVariables, appKey, options);
|
|
408
|
-
|
|
409
|
-
// Validate configuration (pass original appKey for error messages)
|
|
125
|
+
// Extract and validate configuration
|
|
126
|
+
const appConfig = await extractAppConfiguration(finalVariables, appKey, options);
|
|
410
127
|
await validateAppRegistrationData(appConfig, appKey);
|
|
411
128
|
|
|
412
|
-
//
|
|
129
|
+
// Authenticate and get API configuration
|
|
413
130
|
const controllerUrl = finalVariables?.deployment?.controllerUrl;
|
|
414
|
-
|
|
415
|
-
// Check authentication (try device token first, supports registration flow)
|
|
416
131
|
const authConfig = await checkAuthentication(controllerUrl, options.environment);
|
|
417
|
-
|
|
418
|
-
// Validate environment
|
|
419
132
|
const environment = registerApplicationSchema.environmentId(options.environment);
|
|
420
133
|
|
|
421
|
-
// Prepare registration data to match OpenAPI RegisterApplicationRequest schema
|
|
422
|
-
// Schema: { key, displayName, description?, configuration: { type, registryMode, port?, image? } }
|
|
423
|
-
const registrationData = {
|
|
424
|
-
key: appConfig.appKey,
|
|
425
|
-
displayName: appConfig.displayName,
|
|
426
|
-
configuration: {
|
|
427
|
-
type: appConfig.appType,
|
|
428
|
-
registryMode: appConfig.registryMode
|
|
429
|
-
}
|
|
430
|
-
};
|
|
431
|
-
|
|
432
|
-
// Add optional fields only if they have values
|
|
433
|
-
if (appConfig.description || options.description) {
|
|
434
|
-
registrationData.description = appConfig.description || options.description;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
if (appConfig.port) {
|
|
438
|
-
registrationData.configuration.port = appConfig.port;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
134
|
// Register application
|
|
135
|
+
const registrationData = buildRegistrationData(appConfig, options);
|
|
442
136
|
const responseData = await callRegisterApi(
|
|
443
137
|
authConfig.apiUrl,
|
|
444
138
|
authConfig.token,
|
|
@@ -446,35 +140,8 @@ async function registerApplication(appKey, options) {
|
|
|
446
140
|
registrationData
|
|
447
141
|
);
|
|
448
142
|
|
|
449
|
-
// Save credentials
|
|
450
|
-
|
|
451
|
-
const registeredAppKey = responseData.application.key;
|
|
452
|
-
const clientIdKey = `${registeredAppKey}-client-idKeyVault`;
|
|
453
|
-
const clientSecretKey = `${registeredAppKey}-client-secretKeyVault`;
|
|
454
|
-
|
|
455
|
-
try {
|
|
456
|
-
await saveLocalSecret(clientIdKey, responseData.credentials.clientId);
|
|
457
|
-
await saveLocalSecret(clientSecretKey, responseData.credentials.clientSecret);
|
|
458
|
-
|
|
459
|
-
// Update env.template
|
|
460
|
-
await updateEnvTemplate(registeredAppKey, clientIdKey, clientSecretKey, authConfig.apiUrl);
|
|
461
|
-
|
|
462
|
-
// Regenerate .env file with updated credentials
|
|
463
|
-
try {
|
|
464
|
-
await generateEnvFile(registeredAppKey, null, 'local');
|
|
465
|
-
logger.log(chalk.green('ā .env file updated with new credentials'));
|
|
466
|
-
} catch (error) {
|
|
467
|
-
logger.warn(chalk.yellow(`ā ļø Could not regenerate .env file: ${error.message}`));
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
logger.log(chalk.green('\nā Credentials saved to ~/.aifabrix/secrets.local.yaml'));
|
|
471
|
-
logger.log(chalk.green('ā env.template updated with MISO_CLIENTID, MISO_CLIENTSECRET, and MISO_CONTROLLER_URL\n'));
|
|
472
|
-
} catch (error) {
|
|
473
|
-
logger.warn(chalk.yellow(`ā ļø Could not save credentials locally: ${error.message}`));
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
// Display results
|
|
143
|
+
// Save credentials and display results
|
|
144
|
+
await saveLocalCredentials(responseData, authConfig.apiUrl);
|
|
478
145
|
displayRegistrationResults(responseData, authConfig.apiUrl, environment);
|
|
479
146
|
}
|
|
480
147
|
|