@aifabrix/builder 2.39.0 → 2.39.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.
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deploy status display helpers: build app URL from controller/port and show after deploy.
|
|
3
|
+
* @fileoverview Status URL display for application deployment
|
|
4
|
+
* @author AI Fabrix Team
|
|
5
|
+
* @version 2.0.0
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const chalk = require('chalk');
|
|
9
|
+
const logger = require('../utils/logger');
|
|
10
|
+
const { getApplicationStatus } = require('../api/applications.api');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Builds app URL from controller base URL and app port (e.g. http://localhost:3600 + 3601 -> http://localhost:3601).
|
|
14
|
+
* @param {string} controllerUrl - Controller base URL
|
|
15
|
+
* @param {number} port - Application port
|
|
16
|
+
* @returns {string|null} App URL or null if parsing fails
|
|
17
|
+
*/
|
|
18
|
+
function buildAppUrlFromControllerAndPort(controllerUrl, port) {
|
|
19
|
+
if (!controllerUrl || (port === null || port === undefined) || typeof port !== 'number') return null;
|
|
20
|
+
try {
|
|
21
|
+
const u = new URL(controllerUrl);
|
|
22
|
+
u.port = String(port);
|
|
23
|
+
return u.toString();
|
|
24
|
+
} catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Parses URL and port from application status response body.
|
|
31
|
+
* @param {Object} body - Response body (may have data wrapper or top-level url/port)
|
|
32
|
+
* @returns {{ url: string|null, port: number|null }}
|
|
33
|
+
*/
|
|
34
|
+
function parseUrlAndPortFromStatusBody(body) {
|
|
35
|
+
const data = body?.data ?? body;
|
|
36
|
+
const url = (data && typeof data.url === 'string' && data.url.trim() !== '')
|
|
37
|
+
? data.url
|
|
38
|
+
: (body?.url && typeof body.url === 'string' && body.url.trim() !== '')
|
|
39
|
+
? body.url
|
|
40
|
+
: null;
|
|
41
|
+
const port = (data && typeof data.port === 'number')
|
|
42
|
+
? data.port
|
|
43
|
+
: (body && typeof body.port === 'number')
|
|
44
|
+
? body.port
|
|
45
|
+
: null;
|
|
46
|
+
return { url, port };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Fetches app URL from controller application status and displays it.
|
|
51
|
+
* Uses status API url when present; otherwise derives from status port and controller host.
|
|
52
|
+
* @param {string} controllerUrl - Controller base URL (used only to derive host when status returns port)
|
|
53
|
+
* @param {string} envKey - Environment key
|
|
54
|
+
* @param {string} appKey - Application key (manifest.key)
|
|
55
|
+
* @param {Object} authConfig - Auth used for deployment (same as for status)
|
|
56
|
+
*/
|
|
57
|
+
async function displayAppUrlFromController(controllerUrl, envKey, appKey, authConfig) {
|
|
58
|
+
let url = null;
|
|
59
|
+
let port = null;
|
|
60
|
+
try {
|
|
61
|
+
const res = await getApplicationStatus(controllerUrl, envKey, appKey, authConfig);
|
|
62
|
+
const parsed = parseUrlAndPortFromStatusBody(res?.data);
|
|
63
|
+
url = parsed.url;
|
|
64
|
+
port = parsed.port;
|
|
65
|
+
} catch (_) {
|
|
66
|
+
// Show fallback message below
|
|
67
|
+
}
|
|
68
|
+
if (!url && (port !== null && port !== undefined)) {
|
|
69
|
+
url = buildAppUrlFromControllerAndPort(controllerUrl, port);
|
|
70
|
+
}
|
|
71
|
+
if (url) {
|
|
72
|
+
logger.log(chalk.green(` ✓ App running at ${url}`));
|
|
73
|
+
} else {
|
|
74
|
+
logger.log(chalk.blue(' ✓ App deployed. Get URL from controller dashboard.'));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = { displayAppUrlFromController, buildAppUrlFromControllerAndPort, parseUrlAndPortFromStatusBody };
|
package/lib/app/deploy.js
CHANGED
|
@@ -17,8 +17,8 @@ const pushUtils = require('../deployment/push');
|
|
|
17
17
|
const logger = require('../utils/logger');
|
|
18
18
|
const { detectAppType, getBuilderPath, getIntegrationPath } = require('../utils/paths');
|
|
19
19
|
const { checkApplicationExists } = require('../utils/app-existence');
|
|
20
|
-
const { getApplicationStatus } = require('../api/applications.api');
|
|
21
20
|
const { loadDeploymentConfig } = require('./deploy-config');
|
|
21
|
+
const { displayAppUrlFromController } = require('./deploy-status-display');
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* Validate application name format
|
|
@@ -219,26 +219,6 @@ function displayDeploymentResults(result) {
|
|
|
219
219
|
}
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
-
/**
|
|
223
|
-
* Fetches app URL from controller application status and displays it.
|
|
224
|
-
* On API failure or missing URL, shows controllerUrl so the user always sees where the app is.
|
|
225
|
-
* @param {string} controllerUrl - Controller base URL (used as fallback)
|
|
226
|
-
* @param {string} envKey - Environment key
|
|
227
|
-
* @param {string} appKey - Application key (manifest.key)
|
|
228
|
-
* @param {Object} authConfig - Auth used for deployment (same as for status)
|
|
229
|
-
*/
|
|
230
|
-
async function displayAppUrlFromController(controllerUrl, envKey, appKey, authConfig) {
|
|
231
|
-
let url = null;
|
|
232
|
-
try {
|
|
233
|
-
const res = await getApplicationStatus(controllerUrl, envKey, appKey, authConfig);
|
|
234
|
-
const body = res?.data;
|
|
235
|
-
url = (body && (body.url || body.data?.url)) || res?.url || null;
|
|
236
|
-
} catch (_) {
|
|
237
|
-
// Use controllerUrl fallback below
|
|
238
|
-
}
|
|
239
|
-
logger.log(chalk.green(` ✓ App running at ${url || controllerUrl}`));
|
|
240
|
-
}
|
|
241
|
-
|
|
242
222
|
/**
|
|
243
223
|
* Check if app is external and handle external deployment.
|
|
244
224
|
* When options.type === 'external', forces deployment from integration/<app> (no app register needed).
|
|
@@ -19,17 +19,26 @@ const { runServiceUserCreate } = require('../commands/service-user');
|
|
|
19
19
|
function setupServiceUserCommands(program) {
|
|
20
20
|
const serviceUser = program
|
|
21
21
|
.command('service-user')
|
|
22
|
-
.description('Create service users for integrations
|
|
22
|
+
.description('Create and manage service users (API clients) for integrations and CI')
|
|
23
|
+
.addHelpText('after', `
|
|
24
|
+
Service users are dedicated accounts for integrations, CI pipelines, or API clients.
|
|
25
|
+
The controller returns a one-time clientSecret on create—save it immediately; it cannot be retrieved again.
|
|
26
|
+
|
|
27
|
+
Example:
|
|
28
|
+
$ aifabrix service-user create -u api-client-001 -e api@example.com \\
|
|
29
|
+
--redirect-uris "https://app.example.com/callback" --group-names "AI-Fabrix-Developers"
|
|
30
|
+
|
|
31
|
+
Required: permission service-user:create on the controller. Run "aifabrix login" first.`);
|
|
23
32
|
|
|
24
33
|
serviceUser
|
|
25
34
|
.command('create')
|
|
26
|
-
.description('Create a service user
|
|
27
|
-
.option('--controller <url>', 'Controller URL (default: from config)')
|
|
35
|
+
.description('Create a service user and receive a one-time clientSecret (save it now; it will not be shown again)')
|
|
36
|
+
.option('--controller <url>', 'Controller base URL (default: from config)')
|
|
28
37
|
.option('-u, --username <username>', 'Service user username (required)')
|
|
29
38
|
.option('-e, --email <email>', 'Email address (required)')
|
|
30
|
-
.option('--redirect-uris <uris>', 'Comma-separated redirect URIs
|
|
31
|
-
.option('--group-names <names>', 'Comma-separated group names (required
|
|
32
|
-
.option('-d, --description <description>', '
|
|
39
|
+
.option('--redirect-uris <uris>', 'Comma-separated OAuth2 redirect URIs (required)')
|
|
40
|
+
.option('--group-names <names>', 'Comma-separated group names to assign (required)')
|
|
41
|
+
.option('-d, --description <description>', 'Description for the service user')
|
|
33
42
|
.action(async(options) => {
|
|
34
43
|
try {
|
|
35
44
|
const opts = {
|
|
@@ -36,16 +36,22 @@ async function getServiceUserAuth(controllerUrl) {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
|
-
* Extract clientId and clientSecret from API response
|
|
40
|
-
*
|
|
39
|
+
* Extract clientId and clientSecret from API response.
|
|
40
|
+
* Controller returns { data: { user, clientSecret } }; API client puts body in response.data.
|
|
41
|
+
* So payload is at response.data.data. clientId may be on user.clientId or user.federatedIdentity.keycloakClientId.
|
|
42
|
+
* @param {Object} response - API response (success: true, data: body)
|
|
41
43
|
* @returns {{ clientId: string, clientSecret: string }}
|
|
42
44
|
*/
|
|
43
45
|
function extractCreateResponse(response) {
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
const payload = response?.data?.data ?? response?.data ?? response;
|
|
47
|
+
const user = payload?.user;
|
|
48
|
+
const clientId =
|
|
49
|
+
user?.clientId ??
|
|
50
|
+
user?.federatedIdentity?.keycloakClientId ??
|
|
51
|
+
payload?.clientId ??
|
|
52
|
+
'';
|
|
53
|
+
const clientSecret = payload?.clientSecret ?? '';
|
|
54
|
+
return { clientId, clientSecret };
|
|
49
55
|
}
|
|
50
56
|
|
|
51
57
|
/**
|