@aifabrix/builder 2.22.2 → 2.31.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/jest.config.coverage.js +37 -0
- package/lib/api/pipeline.api.js +10 -9
- package/lib/app-deploy.js +36 -14
- package/lib/app-list.js +191 -71
- package/lib/app-prompts.js +77 -26
- package/lib/app-readme.js +123 -5
- package/lib/app-rotate-secret.js +101 -57
- package/lib/app-run-helpers.js +200 -172
- package/lib/app-run.js +137 -68
- package/lib/audit-logger.js +8 -7
- package/lib/build.js +161 -250
- package/lib/cli.js +73 -65
- package/lib/commands/login.js +45 -31
- package/lib/commands/logout.js +181 -0
- package/lib/commands/secure.js +59 -24
- package/lib/config.js +79 -45
- package/lib/datasource-deploy.js +89 -29
- package/lib/deployer.js +164 -129
- package/lib/diff.js +63 -21
- package/lib/environment-deploy.js +36 -19
- package/lib/external-system-deploy.js +134 -66
- package/lib/external-system-download.js +244 -171
- package/lib/external-system-test.js +199 -164
- package/lib/generator-external.js +145 -72
- package/lib/generator-helpers.js +49 -17
- package/lib/generator-split.js +105 -58
- package/lib/infra.js +101 -131
- package/lib/schema/application-schema.json +895 -896
- package/lib/schema/env-config.yaml +11 -4
- package/lib/template-validator.js +13 -4
- package/lib/utils/api.js +8 -8
- package/lib/utils/app-register-auth.js +36 -18
- package/lib/utils/app-run-containers.js +140 -0
- package/lib/utils/auth-headers.js +6 -6
- package/lib/utils/build-copy.js +60 -2
- package/lib/utils/build-helpers.js +94 -0
- package/lib/utils/cli-utils.js +177 -76
- package/lib/utils/compose-generator.js +12 -2
- package/lib/utils/config-tokens.js +151 -9
- package/lib/utils/deployment-errors.js +137 -69
- package/lib/utils/deployment-validation-helpers.js +103 -0
- package/lib/utils/docker-build.js +57 -0
- package/lib/utils/dockerfile-utils.js +13 -3
- package/lib/utils/env-copy.js +163 -94
- package/lib/utils/env-map.js +226 -86
- package/lib/utils/error-formatters/network-errors.js +0 -1
- package/lib/utils/external-system-display.js +14 -19
- package/lib/utils/external-system-env-helpers.js +107 -0
- package/lib/utils/external-system-test-helpers.js +144 -0
- package/lib/utils/health-check.js +10 -8
- package/lib/utils/infra-status.js +123 -0
- package/lib/utils/paths.js +228 -49
- package/lib/utils/schema-loader.js +125 -57
- package/lib/utils/token-manager.js +3 -3
- package/lib/utils/yaml-preserve.js +55 -16
- package/lib/validate.js +87 -89
- package/package.json +4 -4
- package/scripts/ci-fix.sh +19 -0
- package/scripts/ci-simulate.sh +19 -0
- package/templates/applications/miso-controller/test.yaml +1 -0
- package/templates/python/Dockerfile.hbs +8 -45
- package/templates/typescript/Dockerfile.hbs +8 -42
|
@@ -54,11 +54,8 @@ function displayTestResults(results, verbose = false) {
|
|
|
54
54
|
|
|
55
55
|
if (dsResult.metadataSchemaResults) {
|
|
56
56
|
const ms = dsResult.metadataSchemaResults;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
} else {
|
|
60
|
-
logger.log(chalk.red(' Metadata schema: ✗ Invalid'));
|
|
61
|
-
}
|
|
57
|
+
const statusMsg = ms.valid ? ' Metadata schema: ✓ Valid' : ' Metadata schema: ✗ Invalid';
|
|
58
|
+
logger.log(ms.valid ? chalk.gray(statusMsg) : chalk.red(statusMsg));
|
|
62
59
|
}
|
|
63
60
|
}
|
|
64
61
|
}
|
|
@@ -110,20 +107,18 @@ function displayIntegrationTestResults(results, verbose = false) {
|
|
|
110
107
|
}
|
|
111
108
|
}
|
|
112
109
|
|
|
113
|
-
if (verbose) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
vr.warnings.forEach(warn => logger.log(chalk.yellow(` ⚠ ${warn}`)));
|
|
126
|
-
}
|
|
110
|
+
if (verbose && dsResult.validationResults) {
|
|
111
|
+
const vr = dsResult.validationResults;
|
|
112
|
+
if (vr.isValid) {
|
|
113
|
+
logger.log(chalk.gray(' Validation: ✓ Valid'));
|
|
114
|
+
} else {
|
|
115
|
+
logger.log(chalk.red(' Validation: ✗ Invalid'));
|
|
116
|
+
}
|
|
117
|
+
if (vr.errors && vr.errors.length > 0) {
|
|
118
|
+
vr.errors.forEach(err => logger.log(chalk.red(` - ${err}`)));
|
|
119
|
+
}
|
|
120
|
+
if (vr.warnings && vr.warnings.length > 0) {
|
|
121
|
+
vr.warnings.forEach(warn => logger.log(chalk.yellow(` ⚠ ${warn}`)));
|
|
127
122
|
}
|
|
128
123
|
|
|
129
124
|
if (dsResult.fieldMappingResults) {
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External System Environment Variable Helpers
|
|
3
|
+
*
|
|
4
|
+
* Helper functions for extracting environment variables from external system configurations.
|
|
5
|
+
* Separated from external-system-download.js to maintain file size limits.
|
|
6
|
+
*
|
|
7
|
+
* @fileoverview External system environment variable extraction helpers
|
|
8
|
+
* @author AI Fabrix Team
|
|
9
|
+
* @version 2.0.0
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Extract OAuth2 environment variables
|
|
14
|
+
* @param {Object} oauth2 - OAuth2 configuration
|
|
15
|
+
* @param {string} systemKey - System key
|
|
16
|
+
* @param {Array<string>} lines - Lines array to append to
|
|
17
|
+
*/
|
|
18
|
+
function extractOAuth2EnvVars(oauth2, systemKey, lines) {
|
|
19
|
+
if (oauth2.clientId && oauth2.clientId.includes('{{')) {
|
|
20
|
+
const key = oauth2.clientId.replace(/[{}]/g, '').trim();
|
|
21
|
+
lines.push(`${key}=kv://secrets/${systemKey}/client-id`);
|
|
22
|
+
}
|
|
23
|
+
if (oauth2.clientSecret && oauth2.clientSecret.includes('{{')) {
|
|
24
|
+
const key = oauth2.clientSecret.replace(/[{}]/g, '').trim();
|
|
25
|
+
lines.push(`${key}=kv://secrets/${systemKey}/client-secret`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Extract API Key environment variables
|
|
31
|
+
* @param {Object} apikey - API Key configuration
|
|
32
|
+
* @param {string} systemKey - System key
|
|
33
|
+
* @param {Array<string>} lines - Lines array to append to
|
|
34
|
+
*/
|
|
35
|
+
function extractApiKeyEnvVars(apikey, systemKey, lines) {
|
|
36
|
+
if (apikey.key && apikey.key.includes('{{')) {
|
|
37
|
+
const key = apikey.key.replace(/[{}]/g, '').trim();
|
|
38
|
+
lines.push(`${key}=kv://secrets/${systemKey}/api-key`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Extract Basic Auth environment variables
|
|
44
|
+
* @param {Object} basic - Basic Auth configuration
|
|
45
|
+
* @param {string} systemKey - System key
|
|
46
|
+
* @param {Array<string>} lines - Lines array to append to
|
|
47
|
+
*/
|
|
48
|
+
function extractBasicAuthEnvVars(basic, systemKey, lines) {
|
|
49
|
+
if (basic.username && basic.username.includes('{{')) {
|
|
50
|
+
const key = basic.username.replace(/[{}]/g, '').trim();
|
|
51
|
+
lines.push(`${key}=kv://secrets/${systemKey}/username`);
|
|
52
|
+
}
|
|
53
|
+
if (basic.password && basic.password.includes('{{')) {
|
|
54
|
+
const key = basic.password.replace(/[{}]/g, '').trim();
|
|
55
|
+
lines.push(`${key}=kv://secrets/${systemKey}/password`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Extract authentication environment variables
|
|
61
|
+
* @param {Object} auth - Authentication configuration
|
|
62
|
+
* @param {string} systemKey - System key
|
|
63
|
+
* @param {Array<string>} lines - Lines array to append to
|
|
64
|
+
*/
|
|
65
|
+
function extractAuthEnvVars(auth, systemKey, lines) {
|
|
66
|
+
// OAuth2 configuration
|
|
67
|
+
if (auth.type === 'oauth2' && auth.oauth2) {
|
|
68
|
+
extractOAuth2EnvVars(auth.oauth2, systemKey, lines);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// API Key configuration
|
|
72
|
+
if (auth.type === 'apikey' && auth.apikey) {
|
|
73
|
+
extractApiKeyEnvVars(auth.apikey, systemKey, lines);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Basic Auth configuration
|
|
77
|
+
if (auth.type === 'basic' && auth.basic) {
|
|
78
|
+
extractBasicAuthEnvVars(auth.basic, systemKey, lines);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Extracts environment variables from authentication configuration
|
|
84
|
+
* @param {Object} application - External system configuration
|
|
85
|
+
* @returns {string} Environment variables template content
|
|
86
|
+
*/
|
|
87
|
+
function generateEnvTemplate(application) {
|
|
88
|
+
const lines = ['# Environment variables for external system'];
|
|
89
|
+
lines.push(`# System: ${application.key || 'unknown'}`);
|
|
90
|
+
lines.push('');
|
|
91
|
+
|
|
92
|
+
if (!application.authentication) {
|
|
93
|
+
return lines.join('\n');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
extractAuthEnvVars(application.authentication, application.key, lines);
|
|
97
|
+
return lines.join('\n');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
module.exports = {
|
|
101
|
+
extractOAuth2EnvVars,
|
|
102
|
+
extractApiKeyEnvVars,
|
|
103
|
+
extractBasicAuthEnvVars,
|
|
104
|
+
extractAuthEnvVars,
|
|
105
|
+
generateEnvTemplate
|
|
106
|
+
};
|
|
107
|
+
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External System Test Helper Functions
|
|
3
|
+
*
|
|
4
|
+
* Helper functions for external system testing.
|
|
5
|
+
* Separated from external-system-test.js to maintain file size limits.
|
|
6
|
+
*
|
|
7
|
+
* @fileoverview External system test helper functions
|
|
8
|
+
* @author AI Fabrix Team
|
|
9
|
+
* @version 2.0.0
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const fs = require('fs').promises;
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const { testDatasourceViaPipeline } = require('../api/pipeline.api');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Retry API call with exponential backoff
|
|
18
|
+
* @async
|
|
19
|
+
* @param {Function} fn - Function to retry
|
|
20
|
+
* @param {number} maxRetries - Maximum number of retries
|
|
21
|
+
* @param {number} backoffMs - Initial backoff delay in milliseconds
|
|
22
|
+
* @returns {Promise<any>} Function result
|
|
23
|
+
* @throws {Error} Last error if all retries fail
|
|
24
|
+
*/
|
|
25
|
+
async function retryApiCall(fn, maxRetries = 3, backoffMs = 1000) {
|
|
26
|
+
let lastError;
|
|
27
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
28
|
+
try {
|
|
29
|
+
return await fn();
|
|
30
|
+
} catch (error) {
|
|
31
|
+
lastError = error;
|
|
32
|
+
if (attempt < maxRetries) {
|
|
33
|
+
const delay = backoffMs * Math.pow(2, attempt);
|
|
34
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
throw lastError;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Calls pipeline test endpoint using centralized API client
|
|
43
|
+
* @async
|
|
44
|
+
* @param {Object} params - Function parameters
|
|
45
|
+
* @param {string} params.systemKey - System key
|
|
46
|
+
* @param {string} params.datasourceKey - Datasource key
|
|
47
|
+
* @param {Object} params.payloadTemplate - Test payload template
|
|
48
|
+
* @param {string} params.dataplaneUrl - Dataplane URL
|
|
49
|
+
* @param {Object} params.authConfig - Authentication configuration
|
|
50
|
+
* @param {number} [params.timeout] - Request timeout in milliseconds (default: 30000)
|
|
51
|
+
* @returns {Promise<Object>} Test response
|
|
52
|
+
*/
|
|
53
|
+
async function callPipelineTestEndpoint({ systemKey, datasourceKey, payloadTemplate, dataplaneUrl, authConfig, timeout = 30000 }) {
|
|
54
|
+
const response = await retryApiCall(async() => {
|
|
55
|
+
return await testDatasourceViaPipeline({
|
|
56
|
+
dataplaneUrl,
|
|
57
|
+
systemKey,
|
|
58
|
+
datasourceKey,
|
|
59
|
+
authConfig,
|
|
60
|
+
testData: { payloadTemplate },
|
|
61
|
+
options: { timeout }
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
if (!response.success || !response.data) {
|
|
66
|
+
throw new Error(`Test endpoint failed: ${response.error || response.formattedError || 'Unknown error'}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return response.data.data || response.data;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Load custom payload from file if provided
|
|
74
|
+
* @async
|
|
75
|
+
* @param {string} [payloadPath] - Path to custom payload file
|
|
76
|
+
* @returns {Promise<Object|null>} Custom payload or null
|
|
77
|
+
*/
|
|
78
|
+
async function loadCustomPayload(payloadPath) {
|
|
79
|
+
if (!payloadPath) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const absolutePath = path.isAbsolute(payloadPath) ? payloadPath : path.join(process.cwd(), payloadPath);
|
|
84
|
+
const payloadContent = await fs.readFile(absolutePath, 'utf8');
|
|
85
|
+
return JSON.parse(payloadContent);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Determine payload template for a datasource
|
|
90
|
+
* @param {Object} datasource - Datasource configuration
|
|
91
|
+
* @param {string} datasourceKey - Datasource key
|
|
92
|
+
* @param {Object|null} customPayload - Custom payload if provided
|
|
93
|
+
* @returns {Object|null} Payload template or null
|
|
94
|
+
*/
|
|
95
|
+
function determinePayloadTemplate(datasource, datasourceKey, customPayload) {
|
|
96
|
+
if (customPayload) {
|
|
97
|
+
return customPayload;
|
|
98
|
+
}
|
|
99
|
+
if (datasource.testPayload && datasource.testPayload.payloadTemplate) {
|
|
100
|
+
return datasource.testPayload.payloadTemplate;
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Test a single datasource via pipeline
|
|
107
|
+
* @async
|
|
108
|
+
* @param {Object} params - Test parameters
|
|
109
|
+
* @param {string} params.systemKey - System key
|
|
110
|
+
* @param {string} params.datasourceKey - Datasource key
|
|
111
|
+
* @param {Object} params.payloadTemplate - Payload template
|
|
112
|
+
* @param {string} params.dataplaneUrl - Dataplane URL
|
|
113
|
+
* @param {Object} params.authConfig - Authentication configuration
|
|
114
|
+
* @param {number} params.timeout - Request timeout
|
|
115
|
+
* @returns {Promise<Object>} Test result
|
|
116
|
+
*/
|
|
117
|
+
async function testSingleDatasource({ systemKey, datasourceKey, payloadTemplate, dataplaneUrl, authConfig, timeout }) {
|
|
118
|
+
const testResponse = await callPipelineTestEndpoint({
|
|
119
|
+
systemKey,
|
|
120
|
+
datasourceKey,
|
|
121
|
+
payloadTemplate,
|
|
122
|
+
dataplaneUrl,
|
|
123
|
+
authConfig,
|
|
124
|
+
timeout
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
key: datasourceKey,
|
|
129
|
+
skipped: false,
|
|
130
|
+
success: testResponse.success !== false,
|
|
131
|
+
validationResults: testResponse.validationResults || {},
|
|
132
|
+
fieldMappingResults: testResponse.fieldMappingResults || {},
|
|
133
|
+
endpointTestResults: testResponse.endpointTestResults || {}
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
module.exports = {
|
|
138
|
+
retryApiCall,
|
|
139
|
+
callPipelineTestEndpoint,
|
|
140
|
+
loadCustomPayload,
|
|
141
|
+
determinePayloadTemplate,
|
|
142
|
+
testSingleDatasource
|
|
143
|
+
};
|
|
144
|
+
|
|
@@ -99,15 +99,17 @@ async function getContainerPort(appName, debug = false) {
|
|
|
99
99
|
}
|
|
100
100
|
const { stdout: psOutput } = await execAsync(psCmd);
|
|
101
101
|
const portMatch = psOutput.match(/:(\d+)->/);
|
|
102
|
-
if (portMatch) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
102
|
+
if (!portMatch) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
const port = parseInt(portMatch[1], 10);
|
|
106
|
+
if (isNaN(port) || port <= 0) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
if (debug) {
|
|
110
|
+
logger.log(chalk.gray(`[DEBUG] Detected port ${port} from docker ps`));
|
|
110
111
|
}
|
|
112
|
+
return port;
|
|
111
113
|
} catch (error) {
|
|
112
114
|
if (debug) {
|
|
113
115
|
logger.log(chalk.gray(`[DEBUG] Fallback port detection failed: ${error.message}`));
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Fabrix Builder - Infrastructure Status Helpers
|
|
3
|
+
*
|
|
4
|
+
* Status-related helper functions for infrastructure management.
|
|
5
|
+
* Extracted from infra.js to reduce file size.
|
|
6
|
+
*
|
|
7
|
+
* @fileoverview Status helper functions for infrastructure management
|
|
8
|
+
* @author AI Fabrix Team
|
|
9
|
+
* @version 2.0.0
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const { exec } = require('child_process');
|
|
13
|
+
const { promisify } = require('util');
|
|
14
|
+
const config = require('../config');
|
|
15
|
+
const devConfig = require('./dev-config');
|
|
16
|
+
const containerUtils = require('./infra-containers');
|
|
17
|
+
|
|
18
|
+
const execAsync = promisify(exec);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Gets the status of infrastructure services
|
|
22
|
+
* Returns detailed information about running containers
|
|
23
|
+
*
|
|
24
|
+
* @async
|
|
25
|
+
* @function getInfraStatus
|
|
26
|
+
* @returns {Promise<Object>} Status information for each service
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* const status = await getInfraStatus();
|
|
30
|
+
* // Returns: { postgres: { status: 'running', port: 5432, url: 'localhost:5432' }, ... }
|
|
31
|
+
*/
|
|
32
|
+
async function getInfraStatus() {
|
|
33
|
+
const devId = await config.getDeveloperId();
|
|
34
|
+
// Convert string developer ID to number for getDevPorts
|
|
35
|
+
const devIdNum = parseInt(devId, 10);
|
|
36
|
+
const ports = devConfig.getDevPorts(devIdNum);
|
|
37
|
+
const services = {
|
|
38
|
+
postgres: { port: ports.postgres, url: `localhost:${ports.postgres}` },
|
|
39
|
+
redis: { port: ports.redis, url: `localhost:${ports.redis}` },
|
|
40
|
+
pgadmin: { port: ports.pgadmin, url: `http://localhost:${ports.pgadmin}` },
|
|
41
|
+
'redis-commander': { port: ports.redisCommander, url: `http://localhost:${ports.redisCommander}` }
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const status = {};
|
|
45
|
+
|
|
46
|
+
for (const [serviceName, serviceConfig] of Object.entries(services)) {
|
|
47
|
+
try {
|
|
48
|
+
const containerName = await containerUtils.findContainer(serviceName, devId);
|
|
49
|
+
if (containerName) {
|
|
50
|
+
const { stdout } = await execAsync(`docker inspect --format='{{.State.Status}}' ${containerName}`);
|
|
51
|
+
// Normalize status value (trim whitespace and remove quotes)
|
|
52
|
+
const normalizedStatus = stdout.trim().replace(/['"]/g, '');
|
|
53
|
+
status[serviceName] = {
|
|
54
|
+
status: normalizedStatus,
|
|
55
|
+
port: serviceConfig.port,
|
|
56
|
+
url: serviceConfig.url
|
|
57
|
+
};
|
|
58
|
+
} else {
|
|
59
|
+
status[serviceName] = {
|
|
60
|
+
status: 'not running',
|
|
61
|
+
port: serviceConfig.port,
|
|
62
|
+
url: serviceConfig.url
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
} catch (error) {
|
|
66
|
+
status[serviceName] = {
|
|
67
|
+
status: 'not running',
|
|
68
|
+
port: serviceConfig.port,
|
|
69
|
+
url: serviceConfig.url
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return status;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Gets status of running application containers
|
|
79
|
+
* Finds all containers matching pattern aifabrix-dev{id}-* (excluding infrastructure)
|
|
80
|
+
*
|
|
81
|
+
* @async
|
|
82
|
+
* @function getAppStatus
|
|
83
|
+
* @returns {Promise<Array>} Array of application status objects
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* const apps = await getAppStatus();
|
|
87
|
+
* // Returns: [{ name: 'myapp', container: 'aifabrix-dev1-myapp', port: '3100:3000', status: 'running', url: 'http://localhost:3100' }]
|
|
88
|
+
*/
|
|
89
|
+
async function getAppStatus() {
|
|
90
|
+
const devId = await config.getDeveloperId();
|
|
91
|
+
const apps = [];
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
const filterPattern = devId === 0 ? 'aifabrix-' : `aifabrix-dev${devId}-`;
|
|
95
|
+
const { stdout } = await execAsync(`docker ps --filter "name=${filterPattern}" --format "{{.Names}}\t{{.Ports}}\t{{.Status}}"`);
|
|
96
|
+
const lines = stdout.trim().split('\n').filter(line => line.trim() !== '');
|
|
97
|
+
const infraContainers = devId === 0
|
|
98
|
+
? ['aifabrix-postgres', 'aifabrix-redis', 'aifabrix-pgadmin', 'aifabrix-redis-commander']
|
|
99
|
+
: [`aifabrix-dev${devId}-postgres`, `aifabrix-dev${devId}-redis`, `aifabrix-dev${devId}-pgadmin`, `aifabrix-dev${devId}-redis-commander`];
|
|
100
|
+
for (const line of lines) {
|
|
101
|
+
const [containerName, ports, status] = line.split('\t');
|
|
102
|
+
if (infraContainers.includes(containerName)) continue;
|
|
103
|
+
const pattern = devId === 0 ? /^aifabrix-(.+)$/ : new RegExp(`^aifabrix-dev${devId}-(.+)$`);
|
|
104
|
+
const appNameMatch = containerName.match(pattern);
|
|
105
|
+
if (!appNameMatch) continue;
|
|
106
|
+
const appName = appNameMatch[1];
|
|
107
|
+
const portMatch = ports.match(/:(\d+)->\d+\//);
|
|
108
|
+
const hostPort = portMatch ? portMatch[1] : 'unknown';
|
|
109
|
+
const url = hostPort !== 'unknown' ? `http://localhost:${hostPort}` : 'unknown';
|
|
110
|
+
apps.push({ name: appName, container: containerName, port: ports, status: status.trim(), url: url });
|
|
111
|
+
}
|
|
112
|
+
} catch (error) {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return apps;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
module.exports = {
|
|
120
|
+
getInfraStatus,
|
|
121
|
+
getAppStatus
|
|
122
|
+
};
|
|
123
|
+
|