@aifabrix/builder 2.42.1 → 2.43.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/README.md +1 -1
- package/bin/aifabrix.js +1 -1
- package/integration/hubspot-test/README.md +126 -0
- package/integration/{hubspot → hubspot-test}/application.json +6 -6
- package/integration/{hubspot → hubspot-test}/create-hubspot.js +5 -5
- package/integration/hubspot-test/env.template +4 -0
- package/integration/{hubspot/hubspot-datasource-company.json → hubspot-test/hubspot-test-datasource-company.json} +3 -2
- package/integration/{hubspot/hubspot-datasource-contact.json → hubspot-test/hubspot-test-datasource-contact.json} +3 -2
- package/integration/{hubspot/hubspot-datasource-deal.json → hubspot-test/hubspot-test-datasource-deal.json} +3 -2
- package/integration/{hubspot/hubspot-datasource-users.json → hubspot-test/hubspot-test-datasource-users.json} +3 -2
- package/integration/{hubspot/hubspot-deploy.json → hubspot-test/hubspot-test-deploy.json} +198 -21
- package/integration/{hubspot/hubspot-system.json → hubspot-test/hubspot-test-system.json} +8 -7
- package/integration/hubspot-test/rbac.json +166 -0
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-hubspot-credential-real.yaml +3 -3
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-hubspot-env-vars.yaml +2 -2
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-add-datasource.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-credential-create.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-credential-select.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-known-platform.yaml +1 -1
- package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-source.yaml +2 -0
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-mode.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-openapi-file.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-openapi-url.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-source.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-dimension-array-test.yaml +1 -1
- package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-key-test.yaml +5 -0
- package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-path-test.yaml +5 -0
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-dimension-test.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-rbac-test.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-rbac-yaml-test.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test.js +102 -59
- package/integration/{hubspot → hubspot-test}/wizard-hubspot-e2e.yaml +2 -2
- package/integration/{hubspot → hubspot-test}/wizard-hubspot-platform.yaml +1 -1
- package/lib/api/external-test.api.js +1 -1
- package/lib/api/service-users.api.js +111 -2
- package/lib/api/types/service-users.types.js +41 -0
- package/lib/app/register.js +3 -1
- package/lib/app/rotate-secret.js +3 -0
- package/lib/cli/setup-app.js +2 -2
- package/lib/cli/setup-auth.js +19 -11
- package/lib/cli/setup-dev.js +62 -32
- package/lib/cli/setup-environment.js +6 -21
- package/lib/cli/setup-infra.js +13 -0
- package/lib/cli/setup-secrets.js +45 -6
- package/lib/cli/setup-service-user.js +146 -20
- package/lib/cli/setup-utility.js +12 -0
- package/lib/commands/auth-config.js +4 -8
- package/lib/commands/datasource.js +46 -1
- package/lib/commands/dev-init.js +1 -1
- package/lib/commands/repair-env-template.js +14 -8
- package/lib/commands/repair-rbac.js +25 -19
- package/lib/commands/repair.js +96 -30
- package/lib/commands/secrets-remove.js +1 -1
- package/lib/commands/secrets-validate.js +17 -4
- package/lib/commands/service-user.js +231 -2
- package/lib/commands/up-common.js +25 -0
- package/lib/commands/up-dataplane.js +2 -2
- package/lib/core/admin-secrets.js +2 -0
- package/lib/core/config.js +7 -5
- package/lib/core/ensure-encryption-key.js +1 -3
- package/lib/core/secrets.js +32 -9
- package/lib/core/templates.js +1 -1
- package/lib/datasource/abac-validator.js +157 -0
- package/lib/datasource/field-reference-validator.js +74 -36
- package/lib/datasource/log-viewer.js +221 -0
- package/lib/datasource/resolve-app.js +109 -0
- package/lib/datasource/test-e2e.js +11 -20
- package/lib/datasource/test-integration.js +42 -22
- package/lib/datasource/validate.js +5 -2
- package/lib/external-system/generator.js +12 -8
- package/lib/external-system/test-system-level.js +1 -1
- package/lib/generator/external-controller-manifest.js +3 -3
- package/lib/generator/external.js +7 -7
- package/lib/generator/helpers.js +13 -9
- package/lib/generator/index.js +4 -4
- package/lib/generator/split.js +45 -10
- package/lib/generator/wizard.js +9 -6
- package/lib/infrastructure/helpers.js +50 -35
- package/lib/infrastructure/index.js +39 -23
- package/lib/schema/env-config.yaml +19 -2
- package/lib/schema/external-datasource.schema.json +11 -1
- package/lib/utils/app-config-resolver.js +23 -1
- package/lib/utils/config-paths.js +48 -4
- package/lib/utils/credential-secrets-env.js +16 -1
- package/lib/utils/env-map.js +7 -3
- package/lib/utils/error-formatter.js +37 -0
- package/lib/utils/external-env-template.js +180 -0
- package/lib/utils/external-system-display.js +43 -0
- package/lib/utils/external-system-validators.js +2 -2
- package/lib/utils/help-builder.js +3 -5
- package/lib/utils/local-secrets.js +26 -3
- package/lib/utils/paths.js +2 -1
- package/lib/utils/secrets-generator.js +2 -2
- package/lib/utils/secrets-utils.js +4 -0
- package/lib/utils/secure-file-permissions.js +91 -0
- package/lib/utils/token-manager.js +36 -3
- package/lib/utils/yaml-preserve.js +59 -1
- package/lib/validation/env-template-auth.js +50 -2
- package/lib/validation/external-manifest-validator.js +8 -0
- package/lib/validation/validate.js +8 -0
- package/lib/validation/validator.js +10 -13
- package/package.json +5 -1
- package/templates/applications/dataplane/env.template +5 -1
- package/templates/applications/miso-controller/application.yaml +1 -1
- package/templates/applications/miso-controller/env.template +13 -2
- package/templates/external-system/env.template.hbs +22 -0
- package/integration/hubspot/README.md +0 -102
- package/integration/hubspot/env.template +0 -4
- package/integration/hubspot/test-artifacts/wizard-invalid-missing-source.yaml +0 -2
- package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-key-test.yaml +0 -5
- package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-path-test.yaml +0 -5
- /package/integration/{hubspot → hubspot-test}/companies.json +0 -0
- /package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-app-name.yaml +0 -0
- /package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-missing-app.yaml +0 -0
- /package/integration/{hubspot → hubspot-test}/test-dataplane-down-helpers.js +0 -0
- /package/integration/{hubspot → hubspot-test}/test-dataplane-down-tests.js +0 -0
- /package/integration/{hubspot → hubspot-test}/test-dataplane-down.js +0 -0
|
@@ -12,6 +12,7 @@ const fs = require('fs');
|
|
|
12
12
|
const { loadExternalDataSourceSchema } = require('../utils/schema-loader');
|
|
13
13
|
const { formatValidationErrors } = require('../utils/error-formatter');
|
|
14
14
|
const { validateFieldReferences } = require('./field-reference-validator');
|
|
15
|
+
const { validateAbac } = require('./abac-validator');
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* Validates a datasource file against external-datasource schema
|
|
@@ -60,10 +61,12 @@ async function validateDatasourceFile(filePath) {
|
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
const fieldRefErrors = validateFieldReferences(parsed);
|
|
63
|
-
|
|
64
|
+
const abacErrors = validateAbac(parsed);
|
|
65
|
+
const postSchemaErrors = [...fieldRefErrors, ...abacErrors];
|
|
66
|
+
if (postSchemaErrors.length > 0) {
|
|
64
67
|
return {
|
|
65
68
|
valid: false,
|
|
66
|
-
errors:
|
|
69
|
+
errors: postSchemaErrors,
|
|
67
70
|
warnings: []
|
|
68
71
|
};
|
|
69
72
|
}
|
|
@@ -16,6 +16,7 @@ const chalk = require('chalk');
|
|
|
16
16
|
const logger = require('../utils/logger');
|
|
17
17
|
const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
|
|
18
18
|
const { loadConfigFile, writeConfigFile } = require('../utils/config-format');
|
|
19
|
+
const { getKvPathSegmentForSecurityKey } = require('../utils/credential-secrets-env');
|
|
19
20
|
|
|
20
21
|
// Register Handlebars helper for equality check
|
|
21
22
|
handlebars.registerHelper('eq', (a, b) => a === b);
|
|
@@ -23,36 +24,39 @@ handlebars.registerHelper('json', (obj) => JSON.stringify(obj));
|
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* Build authentication object per schema authenticationVariablesByMethod.
|
|
26
|
-
* Security values use kv://<systemKey>/<
|
|
27
|
+
* Security values use canonical kv://<systemKey>/<segment> paths (segment from getKvPathSegmentForSecurityKey).
|
|
27
28
|
* @param {string} systemKey - External system key
|
|
28
29
|
* @param {string} authType - Auth method (oauth2, aad, apikey, basic, queryParam, oidc, hmac, none)
|
|
29
30
|
* @returns {{ method: string, variables: Object, security: Object }} Authentication object
|
|
30
31
|
*/
|
|
31
32
|
function buildAuthenticationFromMethod(systemKey, authType) {
|
|
32
|
-
const
|
|
33
|
+
const kvPath = (securityKey) => {
|
|
34
|
+
const segment = getKvPathSegmentForSecurityKey(securityKey);
|
|
35
|
+
return segment ? `kv://${systemKey}/${segment}` : null;
|
|
36
|
+
};
|
|
33
37
|
const method = authType || 'apikey';
|
|
34
38
|
const base = 'https://api.example.com';
|
|
35
39
|
|
|
36
40
|
const authMap = {
|
|
37
41
|
oauth2: {
|
|
38
42
|
variables: { baseUrl: base, tokenUrl: `${base}/oauth/token`, authorizationUrl: `${base}/oauth/authorize` },
|
|
39
|
-
security: { clientId:
|
|
43
|
+
security: { clientId: kvPath('clientId'), clientSecret: kvPath('clientSecret') }
|
|
40
44
|
},
|
|
41
45
|
aad: {
|
|
42
46
|
variables: { baseUrl: base, tokenUrl: 'https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token', tenantId: '{tenant-id}' },
|
|
43
|
-
security: { clientId:
|
|
47
|
+
security: { clientId: kvPath('clientId'), clientSecret: kvPath('clientSecret') }
|
|
44
48
|
},
|
|
45
49
|
apikey: {
|
|
46
50
|
variables: { baseUrl: base, headerName: 'X-API-Key' },
|
|
47
|
-
security: { apiKey:
|
|
51
|
+
security: { apiKey: kvPath('apiKey') }
|
|
48
52
|
},
|
|
49
53
|
basic: {
|
|
50
54
|
variables: { baseUrl: base },
|
|
51
|
-
security: { username:
|
|
55
|
+
security: { username: kvPath('username'), password: kvPath('password') }
|
|
52
56
|
},
|
|
53
57
|
queryParam: {
|
|
54
58
|
variables: { baseUrl: base, paramName: 'api_key' },
|
|
55
|
-
security: { paramValue:
|
|
59
|
+
security: { paramValue: kvPath('paramValue') }
|
|
56
60
|
},
|
|
57
61
|
oidc: {
|
|
58
62
|
variables: { openIdConfigUrl: 'https://example.com/.well-known/openid-configuration', clientId: 'app-id' },
|
|
@@ -60,7 +64,7 @@ function buildAuthenticationFromMethod(systemKey, authType) {
|
|
|
60
64
|
},
|
|
61
65
|
hmac: {
|
|
62
66
|
variables: { baseUrl: base, algorithm: 'sha256', signatureHeader: 'X-Signature' },
|
|
63
|
-
security: { signingSecret:
|
|
67
|
+
security: { signingSecret: kvPath('signingSecret') }
|
|
64
68
|
},
|
|
65
69
|
none: {
|
|
66
70
|
variables: {},
|
|
@@ -42,7 +42,7 @@ async function runSystemLevelTest({ appName, systemKey, authConfig, dataplaneUrl
|
|
|
42
42
|
let success = true;
|
|
43
43
|
|
|
44
44
|
for (const r of rawResults) {
|
|
45
|
-
const dsKey = r.key || r.datasourceKey;
|
|
45
|
+
const dsKey = r.key || r.sourceKey || r.name || r.datasourceKey;
|
|
46
46
|
const dsResult = {
|
|
47
47
|
key: dsKey,
|
|
48
48
|
success: r.success !== false,
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
const path = require('path');
|
|
13
13
|
const { detectAppType } = require('../utils/paths');
|
|
14
|
-
const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
|
|
14
|
+
const { resolveApplicationConfigPath, resolveRbacPath } = require('../utils/app-config-resolver');
|
|
15
15
|
const { loadSystemFile, loadDatasourceFiles } = require('./external');
|
|
16
16
|
const { loadVariables, loadRbac } = require('./helpers');
|
|
17
17
|
|
|
@@ -77,8 +77,8 @@ function extractAppMetadata(variables, appName) {
|
|
|
77
77
|
*/
|
|
78
78
|
async function loadSystemWithRbac(appPath, schemaBasePath, systemFile) {
|
|
79
79
|
const systemJson = await loadSystemFile(appPath, schemaBasePath, systemFile);
|
|
80
|
-
const rbacPath =
|
|
81
|
-
const rbac = loadRbac(rbacPath);
|
|
80
|
+
const rbacPath = resolveRbacPath(appPath);
|
|
81
|
+
const rbac = rbacPath ? loadRbac(rbacPath) : null;
|
|
82
82
|
mergeRbacIntoSystemJson(systemJson, rbac);
|
|
83
83
|
return systemJson;
|
|
84
84
|
}
|
|
@@ -12,7 +12,7 @@ const fs = require('fs');
|
|
|
12
12
|
const path = require('path');
|
|
13
13
|
const Ajv = require('ajv');
|
|
14
14
|
const { detectAppType, getDeployJsonPath } = require('../utils/paths');
|
|
15
|
-
const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
|
|
15
|
+
const { resolveApplicationConfigPath, resolveRbacPath } = require('../utils/app-config-resolver');
|
|
16
16
|
const { loadConfigFile } = require('../utils/config-format');
|
|
17
17
|
const { loadVariables, loadRbac } = require('./helpers');
|
|
18
18
|
const {
|
|
@@ -117,9 +117,9 @@ async function generateExternalSystemDeployJson(appName, appPath) {
|
|
|
117
117
|
const systemFilePath = resolveSystemFilePath(variables, appPath, appName);
|
|
118
118
|
const systemJson = await loadSystemFileContent(systemFilePath);
|
|
119
119
|
|
|
120
|
-
// Load
|
|
121
|
-
const rbacPath =
|
|
122
|
-
const rbac = loadRbac(rbacPath);
|
|
120
|
+
// Load RBAC from app directory (rbac.yaml, rbac.yml, or rbac.json)
|
|
121
|
+
const rbacPath = resolveRbacPath(appPath);
|
|
122
|
+
const rbac = rbacPath ? loadRbac(rbacPath) : null;
|
|
123
123
|
mergeRbacIntoSystemJson(systemJson, rbac);
|
|
124
124
|
|
|
125
125
|
// Write it as <app-name>-deploy.json (consistent naming)
|
|
@@ -153,9 +153,9 @@ async function loadSystemFile(appPath, schemaBasePath, systemFileName) {
|
|
|
153
153
|
|
|
154
154
|
const systemJson = loadConfigFile(systemFilePath);
|
|
155
155
|
|
|
156
|
-
// Load
|
|
157
|
-
const rbacPath =
|
|
158
|
-
const rbac = loadRbac(rbacPath);
|
|
156
|
+
// Load RBAC from app directory (rbac.yaml, rbac.yml, or rbac.json) and merge if present
|
|
157
|
+
const rbacPath = resolveRbacPath(appPath);
|
|
158
|
+
const rbac = rbacPath ? loadRbac(rbacPath) : null;
|
|
159
159
|
if (rbac) {
|
|
160
160
|
if (rbac.roles && (!systemJson.roles || systemJson.roles.length === 0)) {
|
|
161
161
|
systemJson.roles = rbac.roles;
|
package/lib/generator/helpers.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
const fs = require('fs');
|
|
12
|
-
const
|
|
12
|
+
const path = require('path');
|
|
13
13
|
const { loadConfigFile } = require('../utils/config-format');
|
|
14
14
|
|
|
15
15
|
/**
|
|
@@ -37,21 +37,25 @@ function loadEnvTemplate(templatePath) {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
|
-
* Loads rbac.yaml
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* @
|
|
40
|
+
* Loads RBAC config file (rbac.yaml, rbac.yml, or rbac.json) if it exists.
|
|
41
|
+
* Uses loadConfigFile so format is inferred from extension.
|
|
42
|
+
*
|
|
43
|
+
* @param {string} rbacPath - Path to RBAC file (e.g. from resolveRbacPath)
|
|
44
|
+
* @returns {Object|null} Parsed RBAC configuration or null if path is falsy or file does not exist
|
|
45
|
+
* @throws {Error} If file exists but has invalid syntax (message references actual filename, e.g. rbac.json)
|
|
44
46
|
*/
|
|
45
47
|
function loadRbac(rbacPath) {
|
|
48
|
+
if (!rbacPath || typeof rbacPath !== 'string') {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
46
51
|
if (!fs.existsSync(rbacPath)) {
|
|
47
52
|
return null;
|
|
48
53
|
}
|
|
49
|
-
|
|
50
|
-
const rbacContent = fs.readFileSync(rbacPath, 'utf8');
|
|
51
54
|
try {
|
|
52
|
-
return
|
|
55
|
+
return loadConfigFile(rbacPath);
|
|
53
56
|
} catch (error) {
|
|
54
|
-
|
|
57
|
+
const basename = path.basename(rbacPath);
|
|
58
|
+
throw new Error(`Invalid syntax in ${basename}: ${error.message}`);
|
|
55
59
|
}
|
|
56
60
|
}
|
|
57
61
|
|
package/lib/generator/index.js
CHANGED
|
@@ -13,7 +13,7 @@ const fs = require('fs');
|
|
|
13
13
|
const path = require('path');
|
|
14
14
|
const _validator = require('../validation/validator');
|
|
15
15
|
const builders = require('./builders');
|
|
16
|
-
const { detectAppType, getDeployJsonPath, resolveApplicationConfigPath } = require('../utils/paths');
|
|
16
|
+
const { detectAppType, getDeployJsonPath, resolveApplicationConfigPath, resolveRbacPath } = require('../utils/paths');
|
|
17
17
|
const { logOfflinePathWhenType } = require('../utils/cli-utils');
|
|
18
18
|
const splitFunctions = require('./split');
|
|
19
19
|
const { loadVariables, loadEnvTemplate, loadRbac, parseEnvironmentVariables } = require('./helpers');
|
|
@@ -39,7 +39,7 @@ const { buildEnvVarMap } = require('../utils/env-map');
|
|
|
39
39
|
*
|
|
40
40
|
* @example
|
|
41
41
|
* const jsonPath = await generateDeployJson('myapp');
|
|
42
|
-
* // Returns: './builder/myapp/myapp-deploy.json' or './integration/hubspot/application-schema.json'
|
|
42
|
+
* // Returns: './builder/myapp/myapp-deploy.json' or './integration/hubspot-test/application-schema.json'
|
|
43
43
|
*/
|
|
44
44
|
/**
|
|
45
45
|
* Loads configuration files for deployment generation
|
|
@@ -51,12 +51,12 @@ const { buildEnvVarMap } = require('../utils/env-map');
|
|
|
51
51
|
function loadDeploymentConfigFiles(appPath, appType, appName) {
|
|
52
52
|
const variablesPath = resolveApplicationConfigPath(appPath);
|
|
53
53
|
const templatePath = path.join(appPath, 'env.template');
|
|
54
|
-
const rbacPath =
|
|
54
|
+
const rbacPath = resolveRbacPath(appPath);
|
|
55
55
|
const jsonPath = getDeployJsonPath(appName, appType, true); // Use new naming
|
|
56
56
|
|
|
57
57
|
const { parsed: variables } = loadVariables(variablesPath);
|
|
58
58
|
const envTemplate = loadEnvTemplate(templatePath);
|
|
59
|
-
const rbac = loadRbac(rbacPath);
|
|
59
|
+
const rbac = rbacPath ? loadRbac(rbacPath) : null;
|
|
60
60
|
|
|
61
61
|
return { variables, envTemplate, rbac, jsonPath };
|
|
62
62
|
}
|
package/lib/generator/split.js
CHANGED
|
@@ -14,6 +14,7 @@ const yaml = require('js-yaml');
|
|
|
14
14
|
const { parseImageReference } = require('./parse-image');
|
|
15
15
|
const { generateReadmeFromDeployJson } = require('./split-readme');
|
|
16
16
|
const { extractVariablesYaml, getExternalDatasourceFileName } = require('./split-variables');
|
|
17
|
+
const { generateExternalEnvTemplateContent } = require('../utils/external-env-template');
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Converts configuration array back to env.template format
|
|
@@ -207,24 +208,49 @@ function mergeEnvTemplateWithExisting(existingContent, expectedByKey) {
|
|
|
207
208
|
return updatedLines.join('\n') + (updatedLines.length > 0 ? '\n' : '');
|
|
208
209
|
}
|
|
209
210
|
|
|
211
|
+
/**
|
|
212
|
+
* Builds key -> line map from env.template content (for merge when using external system template).
|
|
213
|
+
* @param {string} content - Full env.template content
|
|
214
|
+
* @returns {Map<string, string>} Key to full KEY=value line
|
|
215
|
+
*/
|
|
216
|
+
function buildExpectedByKeyFromEnvContent(content) {
|
|
217
|
+
const expectedByKey = new Map();
|
|
218
|
+
if (!content || typeof content !== 'string') return expectedByKey;
|
|
219
|
+
const lines = content.split(/\r?\n/);
|
|
220
|
+
for (const line of lines) {
|
|
221
|
+
const trimmed = line.trim();
|
|
222
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
223
|
+
const eq = trimmed.indexOf('=');
|
|
224
|
+
if (eq > 0) {
|
|
225
|
+
const key = trimmed.substring(0, eq).trim();
|
|
226
|
+
expectedByKey.set(key, trimmed);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return expectedByKey;
|
|
230
|
+
}
|
|
231
|
+
|
|
210
232
|
/**
|
|
211
233
|
* Writes env.template (merge or overwrite).
|
|
212
234
|
* @param {string} outputDir - Output directory
|
|
213
235
|
* @param {string} envTemplate - Default env.template content
|
|
214
|
-
* @param {Object} options - Options (mergeEnvTemplate, configuration)
|
|
236
|
+
* @param {Object} options - Options (mergeEnvTemplate, configuration, expectedByKey for external)
|
|
215
237
|
* @returns {Promise<string>} Path to env.template
|
|
216
238
|
*/
|
|
217
239
|
async function writeEnvTemplateToDir(outputDir, envTemplate, options) {
|
|
218
240
|
const envTemplatePath = path.join(outputDir, 'env.template');
|
|
219
241
|
const fsSync = require('fs');
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
242
|
+
const useMerge = options.mergeEnvTemplate && fsSync.existsSync(envTemplatePath);
|
|
243
|
+
if (useMerge) {
|
|
244
|
+
const expectedByKey = options.expectedByKey ||
|
|
245
|
+
(options.configuration ? buildEnvTemplateExpectedByKey(options.configuration) : new Map());
|
|
246
|
+
if (expectedByKey.size > 0) {
|
|
247
|
+
const existingContent = await fs.readFile(envTemplatePath, 'utf8');
|
|
248
|
+
const merged = mergeEnvTemplateWithExisting(existingContent, expectedByKey);
|
|
249
|
+
await writeComponentFile(envTemplatePath, merged);
|
|
250
|
+
return envTemplatePath;
|
|
251
|
+
}
|
|
227
252
|
}
|
|
253
|
+
await writeComponentFile(envTemplatePath, envTemplate);
|
|
228
254
|
return envTemplatePath;
|
|
229
255
|
}
|
|
230
256
|
|
|
@@ -401,12 +427,21 @@ async function splitDeployJson(deployJsonPath, outputDir = null, splitOptions =
|
|
|
401
427
|
normalizeDeploymentForSplit(deployment);
|
|
402
428
|
|
|
403
429
|
const configArray = deployment.configuration || [];
|
|
404
|
-
|
|
430
|
+
let envTemplate;
|
|
431
|
+
const writeOptions = buildSplitWriteOptions(splitOptions, configArray);
|
|
432
|
+
if (deployment.system && typeof deployment.system === 'object') {
|
|
433
|
+
envTemplate = generateExternalEnvTemplateContent(deployment.system);
|
|
434
|
+
if (writeOptions.mergeEnvTemplate) {
|
|
435
|
+
writeOptions.expectedByKey = buildExpectedByKeyFromEnvContent(envTemplate);
|
|
436
|
+
}
|
|
437
|
+
} else {
|
|
438
|
+
envTemplate = extractEnvTemplate(configArray);
|
|
439
|
+
}
|
|
440
|
+
|
|
405
441
|
const variables = extractVariablesYaml(deployment);
|
|
406
442
|
const rbac = extractRbacYaml(deployment);
|
|
407
443
|
const readme = generateReadmeFromDeployJson(deployment);
|
|
408
444
|
|
|
409
|
-
const writeOptions = buildSplitWriteOptions(splitOptions, configArray);
|
|
410
445
|
const result = await writeComponentFiles(finalOutputDir, envTemplate, variables, rbac, readme, writeOptions);
|
|
411
446
|
await applyExternalSystemFilesToResult(finalOutputDir, deployment, result);
|
|
412
447
|
return result;
|
package/lib/generator/wizard.js
CHANGED
|
@@ -16,6 +16,7 @@ const logger = require('../utils/logger');
|
|
|
16
16
|
const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
|
|
17
17
|
const { loadConfigFile, writeConfigFile } = require('../utils/config-format');
|
|
18
18
|
const { systemKeyToKvPrefix, securityKeyToVar, isValidKvPath } = require('../utils/credential-secrets-env');
|
|
19
|
+
const { generateExternalEnvTemplateContent } = require('../utils/external-env-template');
|
|
19
20
|
const { generateReadme } = require('./wizard-readme');
|
|
20
21
|
|
|
21
22
|
/**
|
|
@@ -130,10 +131,12 @@ async function generateConfigFilesForWizard(params) {
|
|
|
130
131
|
format: format || 'yaml'
|
|
131
132
|
});
|
|
132
133
|
|
|
133
|
-
// Generate env.template with
|
|
134
|
-
await generateEnvTemplate(appPath, systemConfig, finalSystemKey);
|
|
135
|
-
|
|
134
|
+
// Generate env.template with Authentication and Configuration sections (Handlebars)
|
|
136
135
|
const envTemplatePath = path.join(appPath, 'env.template');
|
|
136
|
+
const envTemplateContent = generateExternalEnvTemplateContent(systemConfig);
|
|
137
|
+
await fs.writeFile(envTemplatePath, envTemplateContent, 'utf8');
|
|
138
|
+
logger.log(chalk.green('✓ Generated env.template'));
|
|
139
|
+
|
|
137
140
|
try {
|
|
138
141
|
const secretsEnsure = require('../core/secrets-ensure');
|
|
139
142
|
await secretsEnsure.ensureSecretsFromEnvTemplate(envTemplatePath, { emptyValuesForCredentials: true });
|
|
@@ -393,15 +396,15 @@ function addBaseUrlLines(lines, systemConfig) {
|
|
|
393
396
|
}
|
|
394
397
|
|
|
395
398
|
/**
|
|
396
|
-
* Generate env.template with KV_* authentication variables
|
|
399
|
+
* Generate env.template with KV_* authentication variables (legacy; prefer generateExternalEnvTemplateContent).
|
|
397
400
|
* @async
|
|
398
|
-
* @function
|
|
401
|
+
* @function _generateEnvTemplate
|
|
399
402
|
* @param {string} appPath - Application directory path
|
|
400
403
|
* @param {Object} systemConfig - System configuration (must have key for systemKey)
|
|
401
404
|
* @param {string} [finalSystemKey] - Final system key for KV_ prefix (default: systemConfig.key)
|
|
402
405
|
* @throws {Error} If generation fails
|
|
403
406
|
*/
|
|
404
|
-
async function
|
|
407
|
+
async function _generateEnvTemplate(appPath, systemConfig, finalSystemKey) {
|
|
405
408
|
try {
|
|
406
409
|
const envTemplatePath = path.join(appPath, 'env.template');
|
|
407
410
|
const systemKey = finalSystemKey || systemConfig?.key;
|
|
@@ -14,6 +14,7 @@ const fs = require('fs');
|
|
|
14
14
|
const chalk = require('chalk');
|
|
15
15
|
const handlebars = require('handlebars');
|
|
16
16
|
const secrets = require('../core/secrets');
|
|
17
|
+
const adminSecrets = require('../core/admin-secrets');
|
|
17
18
|
const logger = require('../utils/logger');
|
|
18
19
|
const dockerUtils = require('../utils/docker');
|
|
19
20
|
const paths = require('../utils/paths');
|
|
@@ -69,19 +70,6 @@ function logVolumeResetHint(infraDir) {
|
|
|
69
70
|
));
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
/**
|
|
73
|
-
* Apply password to admin-secrets file content (all three password keys).
|
|
74
|
-
* @param {string} content - Current file content
|
|
75
|
-
* @param {string} password - Password to set
|
|
76
|
-
* @returns {string} Updated content
|
|
77
|
-
*/
|
|
78
|
-
function applyPasswordToAdminSecretsContent(content, password) {
|
|
79
|
-
return content
|
|
80
|
-
.replace(/^POSTGRES_PASSWORD=.*$/m, `POSTGRES_PASSWORD=${password}`)
|
|
81
|
-
.replace(/^PGADMIN_DEFAULT_PASSWORD=.*$/m, `PGADMIN_DEFAULT_PASSWORD=${password}`)
|
|
82
|
-
.replace(/^REDIS_COMMANDER_PASSWORD=.*$/m, `REDIS_COMMANDER_PASSWORD=${password}`);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
73
|
/**
|
|
86
74
|
* Sync postgres-passwordKeyVault to the main secrets store (file or remote).
|
|
87
75
|
* @param {string} password - Password to store
|
|
@@ -94,10 +82,42 @@ async function syncPostgresPasswordToStore(password) {
|
|
|
94
82
|
}
|
|
95
83
|
}
|
|
96
84
|
|
|
85
|
+
/** Default admin env keys and values when missing. */
|
|
86
|
+
const DEFAULT_ADMIN_OBJ = {
|
|
87
|
+
PGADMIN_DEFAULT_EMAIL: 'admin@aifabrix.dev',
|
|
88
|
+
REDIS_HOST: 'local:redis:6379:0:',
|
|
89
|
+
REDIS_COMMANDER_USER: 'admin'
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Writes merged admin secrets to disk and logs/syncs as needed.
|
|
94
|
+
* @async
|
|
95
|
+
* @param {string} adminSecretsPath - Path to admin-secrets.env
|
|
96
|
+
* @param {Object} adminObj - Decrypted admin secrets object
|
|
97
|
+
* @param {string} passwordToUse - Password to set for Postgres, pgAdmin, Redis Commander
|
|
98
|
+
* @param {boolean} shouldOverwriteWithAdminPwd - Whether this was an explicit admin password update
|
|
99
|
+
*/
|
|
100
|
+
async function applyAdminSecretsUpdate(adminSecretsPath, adminObj, passwordToUse, shouldOverwriteWithAdminPwd) {
|
|
101
|
+
const merged = { ...DEFAULT_ADMIN_OBJ, ...adminObj };
|
|
102
|
+
merged.POSTGRES_PASSWORD = passwordToUse;
|
|
103
|
+
merged.PGADMIN_DEFAULT_PASSWORD = passwordToUse;
|
|
104
|
+
merged.REDIS_COMMANDER_PASSWORD = passwordToUse;
|
|
105
|
+
const content = await secrets.formatAdminSecretsContent(merged);
|
|
106
|
+
fs.writeFileSync(adminSecretsPath, content, { mode: 0o600 });
|
|
107
|
+
if (shouldOverwriteWithAdminPwd) {
|
|
108
|
+
logger.log('Updated admin password in admin-secrets.env.');
|
|
109
|
+
await syncPostgresPasswordToStore(passwordToUse);
|
|
110
|
+
logVolumeResetHint(path.join(paths.getAifabrixHome(), getInfraDirName(0)));
|
|
111
|
+
} else {
|
|
112
|
+
logger.log('Set default admin password in admin-secrets.env for local use.');
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
97
116
|
/**
|
|
98
117
|
* Ensure admin secrets file exists and set admin password.
|
|
99
118
|
* When adminPwd is provided, update POSTGRES_PASSWORD, PGADMIN_DEFAULT_PASSWORD, REDIS_COMMANDER_PASSWORD
|
|
100
119
|
* in admin-secrets.env (overwrites existing values). Otherwise only backfill empty fields.
|
|
120
|
+
* Reads and writes using decrypted values; writes encrypted when secrets-encryption key is set.
|
|
101
121
|
*
|
|
102
122
|
* @async
|
|
103
123
|
* @param {Object} [options] - Options
|
|
@@ -109,29 +129,25 @@ async function ensureAdminSecrets(options = {}) {
|
|
|
109
129
|
? options.adminPwd.trim()
|
|
110
130
|
: null;
|
|
111
131
|
const passwordToUse = adminPwdOverride || DEFAULT_ADMIN_PASSWORD;
|
|
112
|
-
|
|
113
132
|
const adminSecretsPath = path.join(paths.getAifabrixHome(), 'admin-secrets.env');
|
|
133
|
+
|
|
114
134
|
if (!fs.existsSync(adminSecretsPath)) {
|
|
115
135
|
logger.log('Generating admin-secrets.env...');
|
|
116
136
|
await secrets.generateAdminSecretsEnv(undefined);
|
|
137
|
+
return adminSecretsPath;
|
|
117
138
|
}
|
|
118
|
-
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
139
|
+
|
|
140
|
+
const adminObj = await adminSecrets.readAndDecryptAdminSecrets(adminSecretsPath);
|
|
141
|
+
const needsBackfill = !(adminObj.POSTGRES_PASSWORD && adminObj.POSTGRES_PASSWORD.trim()) ||
|
|
142
|
+
!(adminObj.PGADMIN_DEFAULT_PASSWORD && adminObj.PGADMIN_DEFAULT_PASSWORD.trim()) ||
|
|
143
|
+
!(adminObj.REDIS_COMMANDER_PASSWORD && adminObj.REDIS_COMMANDER_PASSWORD.trim());
|
|
122
144
|
const shouldOverwriteWithAdminPwd = adminPwdOverride !== null;
|
|
123
145
|
|
|
124
|
-
if (shouldOverwriteWithAdminPwd) {
|
|
125
|
-
|
|
126
|
-
fs.writeFileSync(adminSecretsPath, content, { mode: 0o600 });
|
|
127
|
-
logger.log('Updated admin password in admin-secrets.env.');
|
|
128
|
-
await syncPostgresPasswordToStore(passwordToUse);
|
|
129
|
-
logVolumeResetHint(path.join(paths.getAifabrixHome(), getInfraDirName(0)));
|
|
130
|
-
} else if (needsBackfill) {
|
|
131
|
-
content = applyPasswordToAdminSecretsContent(content, passwordToUse);
|
|
132
|
-
fs.writeFileSync(adminSecretsPath, content, { mode: 0o600 });
|
|
133
|
-
logger.log('Set default admin password in admin-secrets.env for local use.');
|
|
146
|
+
if (!shouldOverwriteWithAdminPwd && !needsBackfill) {
|
|
147
|
+
return adminSecretsPath;
|
|
134
148
|
}
|
|
149
|
+
|
|
150
|
+
await applyAdminSecretsUpdate(adminSecretsPath, adminObj, passwordToUse, shouldOverwriteWithAdminPwd);
|
|
135
151
|
return adminSecretsPath;
|
|
136
152
|
}
|
|
137
153
|
|
|
@@ -243,12 +259,13 @@ psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "miso" -c "GRANT AL
|
|
|
243
259
|
}
|
|
244
260
|
|
|
245
261
|
/**
|
|
246
|
-
* Prepare infrastructure directory and extract postgres password
|
|
262
|
+
* Prepare infrastructure directory and extract postgres password from decrypted admin secrets.
|
|
263
|
+
* @async
|
|
247
264
|
* @param {string} devId - Developer ID
|
|
248
265
|
* @param {string} adminSecretsPath - Path to admin secrets file
|
|
249
|
-
* @returns {Object} Object with infraDir and postgresPassword
|
|
266
|
+
* @returns {Promise<Object>} Object with infraDir and postgresPassword
|
|
250
267
|
*/
|
|
251
|
-
function prepareInfraDirectory(devId, adminSecretsPath) {
|
|
268
|
+
async function prepareInfraDirectory(devId, adminSecretsPath) {
|
|
252
269
|
const aifabrixDir = paths.getAifabrixHome();
|
|
253
270
|
const infraDirName = getInfraDirName(devId);
|
|
254
271
|
const infraDir = path.join(aifabrixDir, infraDirName);
|
|
@@ -265,10 +282,8 @@ function prepareInfraDirectory(devId, adminSecretsPath) {
|
|
|
265
282
|
}
|
|
266
283
|
}
|
|
267
284
|
|
|
268
|
-
const
|
|
269
|
-
const
|
|
270
|
-
const raw = postgresPasswordMatch ? postgresPasswordMatch[1] : '';
|
|
271
|
-
const postgresPassword = (raw && raw.trim()) || DEFAULT_ADMIN_PASSWORD;
|
|
285
|
+
const adminObj = await adminSecrets.readAndDecryptAdminSecrets(adminSecretsPath);
|
|
286
|
+
const postgresPassword = (adminObj.POSTGRES_PASSWORD && adminObj.POSTGRES_PASSWORD.trim()) || DEFAULT_ADMIN_PASSWORD;
|
|
272
287
|
generatePgAdminConfig(infraDir, postgresPassword);
|
|
273
288
|
|
|
274
289
|
return { infraDir, postgresPassword };
|
|
@@ -37,8 +37,34 @@ const {
|
|
|
37
37
|
startDockerServicesAndConfigure,
|
|
38
38
|
checkInfraHealth
|
|
39
39
|
} = require('./services');
|
|
40
|
+
const adminSecrets = require('../core/admin-secrets');
|
|
40
41
|
// Lazy require to avoid circular dependency: infra -> app/down -> run-helpers -> infra
|
|
41
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Runs a callback with a temporary .env.run file in infraDir (created from admin-secrets).
|
|
45
|
+
* Removes the file in a finally block.
|
|
46
|
+
* @async
|
|
47
|
+
* @param {string} infraDir - Infrastructure directory path
|
|
48
|
+
* @param {string} adminSecretsPath - Path to admin-secrets.env
|
|
49
|
+
* @param {function(string): Promise<void>} fn - Callback receiving runEnvPath
|
|
50
|
+
* @returns {Promise<void>}
|
|
51
|
+
*/
|
|
52
|
+
async function withRunEnv(infraDir, adminSecretsPath, fn) {
|
|
53
|
+
const runEnvPath = path.join(infraDir, '.env.run');
|
|
54
|
+
try {
|
|
55
|
+
const adminObj = await adminSecrets.readAndDecryptAdminSecrets(adminSecretsPath);
|
|
56
|
+
const content = adminSecrets.envObjectToContent(adminObj);
|
|
57
|
+
fs.writeFileSync(runEnvPath, content, { mode: 0o600 });
|
|
58
|
+
await fn(runEnvPath);
|
|
59
|
+
} finally {
|
|
60
|
+
try {
|
|
61
|
+
if (fs.existsSync(runEnvPath)) fs.unlinkSync(runEnvPath);
|
|
62
|
+
} catch {
|
|
63
|
+
// Ignore unlink errors
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
42
68
|
/**
|
|
43
69
|
* Prepares infrastructure environment
|
|
44
70
|
* Ensures infra secrets exist, then admin-secrets.env, then miso init script.
|
|
@@ -65,7 +91,7 @@ async function prepareInfrastructureEnvironment(developerId, options = {}) {
|
|
|
65
91
|
}
|
|
66
92
|
|
|
67
93
|
// Prepare infrastructure directory
|
|
68
|
-
const { infraDir } = prepareInfraDirectory(devId, adminSecretsPath);
|
|
94
|
+
const { infraDir } = await prepareInfraDirectory(devId, adminSecretsPath);
|
|
69
95
|
await ensureMisoInitScript(infraDir);
|
|
70
96
|
|
|
71
97
|
return { devId, idNum, ports, templatePath, infraDir, adminSecretsPath };
|
|
@@ -174,8 +200,7 @@ async function removeAppVolumes(appNames, devId) {
|
|
|
174
200
|
async function stopInfra() {
|
|
175
201
|
const devId = await config.getDeveloperId();
|
|
176
202
|
const aifabrixDir = paths.getAifabrixHome();
|
|
177
|
-
const
|
|
178
|
-
const infraDir = path.join(aifabrixDir, infraDirName);
|
|
203
|
+
const infraDir = path.join(aifabrixDir, getInfraDirName(devId));
|
|
179
204
|
const composePath = path.join(infraDir, 'compose.yaml');
|
|
180
205
|
const adminSecretsPath = path.join(aifabrixDir, 'admin-secrets.env');
|
|
181
206
|
|
|
@@ -184,17 +209,15 @@ async function stopInfra() {
|
|
|
184
209
|
return;
|
|
185
210
|
}
|
|
186
211
|
|
|
187
|
-
|
|
212
|
+
await withRunEnv(infraDir, adminSecretsPath, async(runEnvPath) => {
|
|
188
213
|
logger.log('Stopping application containers on the same network...');
|
|
189
214
|
await stopAllAppContainers(devId);
|
|
190
215
|
logger.log('Stopping infrastructure services...');
|
|
191
216
|
const projectName = getInfraProjectName(devId);
|
|
192
217
|
const composeCmd = await dockerUtils.getComposeCommand();
|
|
193
|
-
await execAsyncWithCwd(`${composeCmd} -f "${composePath}" -p ${projectName} --env-file "${
|
|
218
|
+
await execAsyncWithCwd(`${composeCmd} -f "${composePath}" -p ${projectName} --env-file "${runEnvPath}" down`, { cwd: infraDir });
|
|
194
219
|
logger.log('Infrastructure services stopped');
|
|
195
|
-
}
|
|
196
|
-
// Keep the compose file for future use
|
|
197
|
-
}
|
|
220
|
+
});
|
|
198
221
|
}
|
|
199
222
|
|
|
200
223
|
/**
|
|
@@ -240,8 +263,7 @@ async function stopAllAppContainersAndVolumes(devId) {
|
|
|
240
263
|
async function stopInfraWithVolumes() {
|
|
241
264
|
const devId = await config.getDeveloperId();
|
|
242
265
|
const aifabrixDir = paths.getAifabrixHome();
|
|
243
|
-
const
|
|
244
|
-
const infraDir = path.join(aifabrixDir, infraDirName);
|
|
266
|
+
const infraDir = path.join(aifabrixDir, getInfraDirName(devId));
|
|
245
267
|
const composePath = path.join(infraDir, 'compose.yaml');
|
|
246
268
|
const adminSecretsPath = path.join(aifabrixDir, 'admin-secrets.env');
|
|
247
269
|
|
|
@@ -250,17 +272,15 @@ async function stopInfraWithVolumes() {
|
|
|
250
272
|
return;
|
|
251
273
|
}
|
|
252
274
|
|
|
253
|
-
|
|
275
|
+
await withRunEnv(infraDir, adminSecretsPath, async(runEnvPath) => {
|
|
254
276
|
logger.log('Stopping application containers on the same network...');
|
|
255
277
|
await stopAllAppContainersAndVolumes(devId);
|
|
256
278
|
logger.log('Stopping infrastructure services and removing all data...');
|
|
257
279
|
const projectName = getInfraProjectName(devId);
|
|
258
280
|
const composeCmd = await dockerUtils.getComposeCommand();
|
|
259
|
-
await execAsyncWithCwd(`${composeCmd} -f "${composePath}" -p ${projectName} --env-file "${
|
|
281
|
+
await execAsyncWithCwd(`${composeCmd} -f "${composePath}" -p ${projectName} --env-file "${runEnvPath}" down -v`, { cwd: infraDir });
|
|
260
282
|
logger.log('Infrastructure services stopped and all data removed');
|
|
261
|
-
}
|
|
262
|
-
// Keep the compose file for future use
|
|
263
|
-
}
|
|
283
|
+
});
|
|
264
284
|
}
|
|
265
285
|
|
|
266
286
|
/**
|
|
@@ -281,7 +301,6 @@ async function restartService(serviceName) {
|
|
|
281
301
|
if (!serviceName || typeof serviceName !== 'string') {
|
|
282
302
|
throw new Error('Service name is required and must be a string');
|
|
283
303
|
}
|
|
284
|
-
|
|
285
304
|
const validServices = ['postgres', 'redis', 'pgadmin', 'redis-commander', 'traefik'];
|
|
286
305
|
if (!validServices.includes(serviceName)) {
|
|
287
306
|
throw new Error(`Invalid service name. Must be one of: ${validServices.join(', ')}`);
|
|
@@ -289,8 +308,7 @@ async function restartService(serviceName) {
|
|
|
289
308
|
|
|
290
309
|
const devId = await config.getDeveloperId();
|
|
291
310
|
const aifabrixDir = paths.getAifabrixHome();
|
|
292
|
-
const
|
|
293
|
-
const infraDir = path.join(aifabrixDir, infraDirName);
|
|
311
|
+
const infraDir = path.join(aifabrixDir, getInfraDirName(devId));
|
|
294
312
|
const composePath = path.join(infraDir, 'compose.yaml');
|
|
295
313
|
const adminSecretsPath = path.join(aifabrixDir, 'admin-secrets.env');
|
|
296
314
|
|
|
@@ -298,15 +316,13 @@ async function restartService(serviceName) {
|
|
|
298
316
|
throw new Error('Infrastructure not properly configured');
|
|
299
317
|
}
|
|
300
318
|
|
|
301
|
-
|
|
319
|
+
await withRunEnv(infraDir, adminSecretsPath, async(runEnvPath) => {
|
|
302
320
|
logger.log(`Restarting ${serviceName} service...`);
|
|
303
321
|
const projectName = getInfraProjectName(devId);
|
|
304
322
|
const composeCmd = await dockerUtils.getComposeCommand();
|
|
305
|
-
await execAsyncWithCwd(`${composeCmd} -f "${composePath}" -p ${projectName} --env-file "${
|
|
323
|
+
await execAsyncWithCwd(`${composeCmd} -f "${composePath}" -p ${projectName} --env-file "${runEnvPath}" restart ${serviceName}`, { cwd: infraDir });
|
|
306
324
|
logger.log(`${serviceName} service restarted successfully`);
|
|
307
|
-
}
|
|
308
|
-
// Keep the compose file for future use
|
|
309
|
-
}
|
|
325
|
+
});
|
|
310
326
|
}
|
|
311
327
|
|
|
312
328
|
// Re-export status helper functions
|