@aifabrix/builder 2.45.0 → 2.46.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/.cursor/rules/cli-layout.mdc +2 -0
- package/.cursor/rules/cli-product-neutral.mdc +43 -0
- package/.cursor/rules/docs-rules.mdc +3 -1
- package/.cursor/rules/project-rules.mdc +1 -0
- package/README.md +43 -17
- package/integration/hubspot-test/wizard-hubspot-e2e.yaml +1 -1
- package/jest.config.default.js +2 -1
- package/jest.config.isolated.js +2 -1
- package/jest.global-live-fabrix-hooks.js +36 -0
- package/jest.isolated-projects.js +130 -0
- package/jest.projects.js +69 -121
- package/lib/api/agent-metadata-validation.api.js +93 -0
- package/lib/api/api-response-errors.js +124 -0
- package/lib/api/audit-api-helpers.js +68 -0
- package/lib/api/audit.api.js +119 -0
- package/lib/api/auth-cache.api.js +44 -0
- package/lib/api/certificates.api.js +4 -4
- package/lib/api/credential.api.js +2 -2
- package/lib/api/dataplane-health.api.js +137 -0
- package/lib/api/dataplane-sync.api.js +27 -0
- package/lib/api/datasources-core.api.js +21 -18
- package/lib/api/datasources-extended.api.js +22 -22
- package/lib/api/dimensions.api.js +3 -0
- package/lib/api/external-systems.api.js +13 -13
- package/lib/api/governance-scenario-pack.api.js +159 -0
- package/lib/api/governance-scenarios.api.js +39 -0
- package/lib/api/groups.api.js +79 -0
- package/lib/api/index.js +88 -5
- package/lib/api/lifecycle.api.js +77 -0
- package/lib/api/pipeline.api.js +7 -5
- package/lib/api/protection.api.js +157 -0
- package/lib/api/records-bulk.api.js +43 -0
- package/lib/api/records-search-parse.js +49 -0
- package/lib/api/records-search.api.js +29 -0
- package/lib/api/types/agent-metadata-validation.types.js +67 -0
- package/lib/api/types/audit.types.js +72 -0
- package/lib/api/types/dataplane-health.types.js +27 -0
- package/lib/api/types/dimensions.types.js +2 -0
- package/lib/api/types/governance-scenarios.types.js +39 -0
- package/lib/api/types/protection.types.js +69 -0
- package/lib/api/types/records-bulk.types.js +35 -0
- package/lib/api/types/records-search.types.js +28 -0
- package/lib/api/user-groups.api.js +50 -0
- package/lib/api/users.api.js +95 -0
- package/lib/api/validation-run.api.js +3 -3
- package/lib/api/validation-runner.js +5 -3
- package/lib/api/wizard-platform.api.js +3 -3
- package/lib/api/wizard.api.js +21 -21
- package/lib/app/deploy-config.js +4 -1
- package/lib/app/deploy.js +34 -19
- package/lib/app/down.js +20 -1
- package/lib/app/push.js +4 -5
- package/lib/app/register.js +7 -4
- package/lib/app/run-env-compose.js +84 -30
- package/lib/build/index.js +5 -0
- package/lib/certification/sync-system-certification.js +13 -6
- package/lib/cli/index.js +8 -2
- package/lib/cli/infra-guided-auth.js +109 -0
- package/lib/cli/infra-guided.js +91 -69
- package/lib/cli/setup-app.help.js +19 -2
- package/lib/cli/setup-app.js +58 -23
- package/lib/cli/setup-app.test-commands.js +28 -21
- package/lib/cli/setup-auth.js +55 -3
- package/lib/cli/setup-certification-commands.js +147 -0
- package/lib/cli/setup-certification.help.js +141 -0
- package/lib/cli/setup-environment.js +19 -2
- package/lib/cli/setup-external-system-help.js +62 -0
- package/lib/cli/setup-external-system.js +62 -6
- package/lib/cli/setup-infra.js +9 -1
- package/lib/cli/setup-platform.js +6 -1
- package/lib/cli/setup-secrets.js +33 -1
- package/lib/cli/setup-utility-help-after.js +65 -0
- package/lib/cli/setup-utility-repair.js +198 -0
- package/lib/cli/setup-utility-resolve.js +42 -6
- package/lib/cli/setup-utility-validate.js +197 -0
- package/lib/cli/setup-utility.js +41 -258
- package/lib/commands/auth-status-client-tokens.js +214 -0
- package/lib/commands/auth-status-dataplane-version.js +269 -0
- package/lib/commands/auth-status-display.js +94 -0
- package/lib/commands/auth-status.js +111 -53
- package/lib/commands/credential-push.js +10 -5
- package/lib/commands/datasource-load-export-cli.js +178 -0
- package/lib/commands/datasource-test-trust-cli.js +118 -0
- package/lib/commands/datasource-unified-test-cli.js +31 -20
- package/lib/commands/datasource-unified-test-cli.options.js +22 -3
- package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +5 -5
- package/lib/commands/datasource-verify-audit-cli.js +96 -0
- package/lib/commands/datasource.js +68 -1
- package/lib/commands/dev-show-display.js +8 -1
- package/lib/commands/dimension-value.js +43 -13
- package/lib/commands/dimension.js +38 -7
- package/lib/commands/governance-cli-shared.js +94 -0
- package/lib/commands/governance-command-action.js +132 -0
- package/lib/commands/governance-download-external.js +34 -0
- package/lib/commands/governance-generate-external.js +71 -0
- package/lib/commands/governance-upload-external.js +36 -0
- package/lib/commands/governance-verify-external.js +64 -0
- package/lib/commands/governance.js +144 -0
- package/lib/commands/identity-apply-sync.js +131 -0
- package/lib/commands/identity-group-cli.js +146 -0
- package/lib/commands/identity-membership-role-cache.js +175 -0
- package/lib/commands/identity-shared.js +94 -0
- package/lib/commands/identity-user-cli.js +158 -0
- package/lib/commands/identity.js +43 -0
- package/lib/commands/lifecycle-command-action.js +123 -0
- package/lib/commands/lifecycle-external.js +268 -0
- package/lib/commands/login.js +56 -17
- package/lib/commands/protection-cli-leaves.js +286 -0
- package/lib/commands/protection.js +50 -0
- package/lib/commands/repair-auth-apply.js +127 -0
- package/lib/commands/repair-changed-files.js +41 -0
- package/lib/commands/repair-datasource-keys.js +4 -1
- package/lib/commands/repair-datasource-run.js +59 -0
- package/lib/commands/repair-datasource-test-payload.js +259 -0
- package/lib/commands/repair-datasource.js +1 -51
- package/lib/commands/repair-env-template.js +64 -13
- package/lib/commands/repair-external-run.js +216 -0
- package/lib/commands/repair-openapi-sync.js +23 -0
- package/lib/commands/repair-persist.js +4 -0
- package/lib/commands/repair-rbac.js +52 -1
- package/lib/commands/repair-steps.js +145 -0
- package/lib/commands/repair.js +29 -286
- package/lib/commands/secrets-get.js +184 -0
- package/lib/commands/secrets-remove-all.js +3 -1
- package/lib/commands/setup-image-refresh.js +146 -0
- package/lib/commands/setup-modes.js +158 -162
- package/lib/commands/setup-platform-auth.js +76 -0
- package/lib/commands/setup-prompts-admin.js +244 -0
- package/lib/commands/setup-prompts-platform-mode.js +67 -0
- package/lib/commands/setup-prompts-validators.js +36 -0
- package/lib/commands/setup-prompts.js +24 -76
- package/lib/commands/setup-run-context.js +45 -0
- package/lib/commands/setup-spinners.js +39 -0
- package/lib/commands/setup.js +27 -5
- package/lib/commands/teardown.js +39 -0
- package/lib/commands/test-e2e-external.js +61 -14
- package/lib/commands/test-governance-cli-options.js +47 -0
- package/lib/commands/test-governance-external.js +67 -0
- package/lib/commands/test-trust-cli-options.js +32 -0
- package/lib/commands/test-trust-external.js +126 -0
- package/lib/commands/up-common.js +25 -13
- package/lib/commands/up-dataplane.js +50 -13
- package/lib/commands/upload-publish-context.js +75 -0
- package/lib/commands/upload-sidecar-followups.js +89 -0
- package/lib/commands/upload.js +108 -105
- package/lib/commands/verify-governance-combined-external.js +61 -0
- package/lib/commands/verify-governance-command-action.js +64 -0
- package/lib/commands/verify-governance-db.js +102 -0
- package/lib/commands/verify-governance-external.js +77 -0
- package/lib/commands/verify-governance-result-builders.js +71 -0
- package/lib/commands/verify-operations-command-action.js +68 -0
- package/lib/commands/verify-operations-external.js +268 -0
- package/lib/commands/verify-operations-persist.js +75 -0
- package/lib/commands/verify-operations-step-plan.js +68 -0
- package/lib/commands/verify-operations-steps.js +111 -0
- package/lib/commands/verify-trust-command-action.js +70 -0
- package/lib/commands/verify-trust-external.js +67 -0
- package/lib/commands/wizard-config-normalizer.js +43 -23
- package/lib/commands/wizard-core.js +5 -0
- package/lib/core/admin-password-generator.js +38 -0
- package/lib/core/admin-secrets-bundle.js +111 -0
- package/lib/core/admin-secrets-env-overlay.js +62 -0
- package/lib/core/admin-secrets.js +32 -4
- package/lib/core/config-attach-extensions.js +3 -0
- package/lib/core/config-device-dataplane.js +185 -0
- package/lib/core/config-platform-controller.js +44 -0
- package/lib/core/config-setup-installation-profile.js +42 -0
- package/lib/core/config-setup-platform-mode.js +46 -0
- package/lib/core/config.js +46 -36
- package/lib/core/secrets-admin-env.js +6 -2
- package/lib/core/secrets-ensure.js +4 -0
- package/lib/core/secrets-env-content.js +54 -48
- package/lib/core/secrets-env-write.js +10 -1
- package/lib/core/secrets-infra-placeholder-sync.js +2 -0
- package/lib/core/setup-installation-profile.js +43 -0
- package/lib/core/setup-platform-mode.js +55 -0
- package/lib/datasource/agent-trust-debug-log.js +45 -0
- package/lib/datasource/agent-trust-fetch.js +115 -0
- package/lib/datasource/agent-trust-map.js +101 -0
- package/lib/datasource/agent-trust-mock.js +44 -0
- package/lib/datasource/agent-trust-run.js +95 -0
- package/lib/datasource/agent-trust-timeout.js +25 -0
- package/lib/datasource/audit-evidence-display.js +80 -0
- package/lib/datasource/audit-evidence-extract-debug.js +115 -0
- package/lib/datasource/audit-evidence-extract.js +180 -0
- package/lib/datasource/audit-evidence-matrix-rows.js +158 -0
- package/lib/datasource/audit-evidence-operation-aliases.js +63 -0
- package/lib/datasource/audit-evidence-poll.js +109 -0
- package/lib/datasource/audit-evidence-run.js +177 -0
- package/lib/datasource/audit-evidence-verify-helpers.js +167 -0
- package/lib/datasource/audit-evidence-verify-matrix.js +205 -0
- package/lib/datasource/audit-evidence-verify.js +108 -0
- package/lib/datasource/bulk-loader-run-batches.js +100 -0
- package/lib/datasource/bulk-loader-service-retry.js +52 -0
- package/lib/datasource/bulk-loader-service.js +127 -0
- package/lib/datasource/capability/run-capability-dimension.js +2 -1
- package/lib/datasource/datasource-exporter-resolve.js +82 -0
- package/lib/datasource/datasource-exporter-service.js +144 -0
- package/lib/datasource/datasource-identifier.js +148 -0
- package/lib/datasource/datasource-load-export-context.js +95 -0
- package/lib/datasource/integration-context.js +6 -4
- package/lib/datasource/local-data-paths.js +145 -0
- package/lib/datasource/log-cleaner.js +241 -0
- package/lib/datasource/log-viewer.js +111 -6
- package/lib/datasource/record-file-parser.js +85 -0
- package/lib/datasource/record-mapper.js +129 -0
- package/lib/datasource/resolve-app.js +74 -55
- package/lib/datasource/test-e2e.js +29 -2
- package/lib/datasource/unified-validation-run-body.js +3 -1
- package/lib/datasource/unified-validation-run-resolve.js +45 -12
- package/lib/datasource/unified-validation-run.js +47 -18
- package/lib/datasource/validate.js +37 -20
- package/lib/deployment/environment-poll-status.js +138 -0
- package/lib/deployment/environment.js +42 -34
- package/lib/external-system/download.js +21 -20
- package/lib/external-system/generator.js +5 -1
- package/lib/external-system/integration-auth-context.js +45 -0
- package/lib/external-system/test-auth.js +12 -5
- package/lib/external-system/test-system-level.js +25 -4
- package/lib/external-system/test.js +9 -7
- package/lib/generator/external-controller-manifest.js +15 -0
- package/lib/governance/governance-cli-errors.js +96 -0
- package/lib/governance/governance-download-display.js +38 -0
- package/lib/governance/governance-pack-loader.js +70 -0
- package/lib/governance/governance-run-display.js +309 -0
- package/lib/identity/identity-apply-core.js +108 -0
- package/lib/identity/identity-apply-phases.js +244 -0
- package/lib/identity/identity-apply-service.js +77 -0
- package/lib/identity/identity-csv-parser.js +114 -0
- package/lib/infrastructure/admin-secrets-apply.js +84 -0
- package/lib/infrastructure/admin-secrets-ensure.js +130 -0
- package/lib/infrastructure/admin-secrets-resolve.js +95 -0
- package/lib/infrastructure/admin-secrets-sync.js +91 -0
- package/lib/infrastructure/helpers.js +23 -129
- package/lib/infrastructure/index.js +5 -6
- package/lib/infrastructure/orphan-infra-docker-teardown.js +11 -5
- package/lib/internal/fs-real-sync.js +29 -1
- package/lib/lifecycle/certification-progress.js +231 -0
- package/lib/lifecycle/dataplane-ui-url.js +145 -0
- package/lib/lifecycle/exit-codes.js +24 -0
- package/lib/lifecycle/json-envelope.js +25 -0
- package/lib/lifecycle/lifecycle-governance-options.js +51 -0
- package/lib/lifecycle/logger-quiet.js +29 -0
- package/lib/lifecycle/open-api-published.js +59 -0
- package/lib/lifecycle/operations-readiness-metrics.js +101 -0
- package/lib/lifecycle/product-model.js +196 -0
- package/lib/lifecycle/report-display.js +471 -0
- package/lib/lifecycle/scenario-upload.js +191 -0
- package/lib/lifecycle/verify-step-progress.js +87 -0
- package/lib/parameters/infra-kv-discovery.js +27 -2
- package/lib/protection/auth-context.js +51 -0
- package/lib/protection/convert-batch.js +90 -0
- package/lib/protection/load.js +41 -0
- package/lib/protection/paths.js +158 -0
- package/lib/protection/preflight-datasource-ready.js +46 -0
- package/lib/protection/protection-create-probe-lines.js +142 -0
- package/lib/protection/protection-create-probes.js +95 -0
- package/lib/protection/protection-create-scaffold.js +281 -0
- package/lib/protection/protection-display-helpers.js +445 -0
- package/lib/protection/protection-display.js +215 -0
- package/lib/protection/protection-preset-registry.js +97 -0
- package/lib/protection/report-exit.js +62 -0
- package/lib/protection/resolve.js +202 -0
- package/lib/protection/run-commands-validate.js +130 -0
- package/lib/protection/run-commands.js +216 -0
- package/lib/protection/run-protection-create-helpers.js +134 -0
- package/lib/protection/run-protection-create-probes-flow.js +43 -0
- package/lib/protection/run-protection-create.js +191 -0
- package/lib/protection/scope.js +22 -0
- package/lib/protection/sync-after-upload.js +64 -0
- package/lib/protection/upload-batch.js +127 -0
- package/lib/protection/validate-batch.js +113 -0
- package/lib/protection/validate-local.js +65 -0
- package/lib/resolvers/dimension-file.js +29 -4
- package/lib/schema/application-schema.json +1 -1
- package/lib/schema/datasource-test-run.schema.json +89 -6
- package/lib/schema/external-datasource.schema.json +2636 -2588
- package/lib/schema/external-system.schema.json +1089 -761
- package/lib/schema/flag-map-validation-run.json +2 -2
- package/lib/schema/infra.parameter.yaml +45 -0
- package/lib/schema/protection.schema.json +374 -0
- package/lib/schema/type/credential-field-registry.json +642 -0
- package/lib/schema/wizard-config.schema.json +1 -1
- package/lib/utils/agent-trust-run-display.js +231 -0
- package/lib/utils/agent-trust-run-exit.js +59 -0
- package/lib/utils/agent-trust-wait-ui.js +94 -0
- package/lib/utils/aifabrix-test-runtime-guard.js +125 -0
- package/lib/utils/api.js +55 -31
- package/lib/utils/app-relative-path-env.js +112 -0
- package/lib/utils/cli-exit-codes.js +30 -0
- package/lib/utils/cli-secrets-error-format.js +83 -21
- package/lib/utils/cli-sync-options.js +33 -0
- package/lib/utils/cli-utils.js +16 -4
- package/lib/utils/compose-generate-docker-compose.js +4 -0
- package/lib/utils/compose-traefik-ingress-base.js +16 -1
- package/lib/utils/config-secrets-path.js +46 -0
- package/lib/utils/controller-url.js +88 -1
- package/lib/utils/credential-push-ui.js +90 -0
- package/lib/utils/credential-secrets-env.js +137 -9
- package/lib/utils/dataplane-cli-version-gate.js +202 -0
- package/lib/utils/dataplane-cli-version-help.js +74 -0
- package/lib/utils/dataplane-resolver.js +129 -2
- package/lib/utils/datasource-export-display.js +137 -0
- package/lib/utils/datasource-load-display.js +174 -0
- package/lib/utils/datasource-load-export-ui.js +120 -0
- package/lib/utils/datasource-test-run-capability-scope.js +3 -3
- package/lib/utils/datasource-test-run-display.js +3 -3
- package/lib/utils/datasource-test-run-tty-log.js +5 -1
- package/lib/utils/declarative-url-ports.js +3 -3
- package/lib/utils/deployment-auth-mode.js +41 -0
- package/lib/utils/developer-volume-id.js +33 -0
- package/lib/utils/env-copy.js +14 -9
- package/lib/utils/env-kv-orphan-lines.js +43 -0
- package/lib/utils/error-formatters/network-errors.js +13 -4
- package/lib/utils/external-env-template.js +1 -0
- package/lib/utils/external-system-readiness-core.js +10 -0
- package/lib/utils/external-system-readiness-display.js +80 -23
- package/lib/utils/health-check.js +4 -1
- package/lib/utils/help-builder.js +35 -26
- package/lib/utils/keycloak-realm-ready.js +101 -0
- package/lib/utils/load-cip-capacity-display-config.js +61 -15
- package/lib/utils/manifest-source-emit.js +24 -0
- package/lib/utils/pipeline-upload-auth-retry.js +82 -0
- package/lib/utils/platform-controller-url.js +156 -0
- package/lib/utils/postgres-platform-bootstrap.js +199 -0
- package/lib/utils/postgres-wipe.js +9 -2
- package/lib/utils/resolve-env-mode.js +47 -0
- package/lib/utils/secrets-generator.js +31 -25
- package/lib/utils/secrets-missing-error.js +164 -0
- package/lib/utils/semver-compare.js +127 -0
- package/lib/utils/service-url-label.js +81 -0
- package/lib/utils/setup-quiet-output.js +25 -0
- package/lib/utils/template-helpers.js +15 -0
- package/lib/utils/token-manager-client-credentials.js +201 -0
- package/lib/utils/token-manager-messages.js +8 -0
- package/lib/utils/token-manager-refresh.js +12 -2
- package/lib/utils/token-manager.js +86 -105
- package/lib/utils/upload-pipeline-ui.js +98 -0
- package/lib/utils/upload-sync-options.js +42 -0
- package/lib/utils/url-declarative-public-base.js +21 -3
- package/lib/utils/url-declarative-vdir-inactive-env.js +14 -4
- package/lib/utils/validation-poll-ui.js +22 -7
- package/lib/utils/validation-run-stage.js +45 -0
- package/lib/validation/wizard-config-validator.js +58 -19
- package/package.json +1 -1
- package/templates/applications/dataplane/application.yaml +2 -1
- package/templates/applications/dataplane/env.template +36 -1
- package/templates/applications/dataplane/rbac.yaml +4 -0
- package/templates/applications/miso-controller/env.template +16 -0
- package/templates/external-system/README.md.hbs +116 -11
- package/templates/python/docker-compose.hbs +19 -18
- package/templates/typescript/docker-compose.hbs +18 -17
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry dataplane pipeline upload with application client token when device Bearer fails.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Pipeline publish auth fallback when device Bearer is rejected
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const chalk = require('chalk');
|
|
12
|
+
const logger = require('./logger');
|
|
13
|
+
const { uploadApplicationViaPipeline } = require('../api/pipeline.api');
|
|
14
|
+
const { getDeploymentAuth } = require('./token-manager');
|
|
15
|
+
const { resolveControllerUrl } = require('./controller-url');
|
|
16
|
+
const config = require('../core/config');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {Object} apiResponse
|
|
20
|
+
* @returns {boolean}
|
|
21
|
+
*/
|
|
22
|
+
function isPipelineAuthFailure(apiResponse) {
|
|
23
|
+
if (!apiResponse || apiResponse.success !== false) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
const status = Number(apiResponse.status) || 0;
|
|
27
|
+
if (status === 401 || status === 403) {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
const text = String(
|
|
31
|
+
apiResponse.formattedError || apiResponse.error || apiResponse.message || ''
|
|
32
|
+
).toLowerCase();
|
|
33
|
+
return text.includes('invalid token') || text.includes('unauthorized') || text.includes('authentication');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {string} systemKey
|
|
38
|
+
* @returns {Promise<Object|null>}
|
|
39
|
+
*/
|
|
40
|
+
async function resolveClientTokenAuthForPipelineRetry(systemKey) {
|
|
41
|
+
try {
|
|
42
|
+
const controllerUrl = await resolveControllerUrl();
|
|
43
|
+
const environment = await config.resolveEnvironment();
|
|
44
|
+
return await getDeploymentAuth(controllerUrl, environment, systemKey, {
|
|
45
|
+
deploymentAuth: 'client-credentials'
|
|
46
|
+
});
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* POST /api/v1/pipeline/upload with optional client-token retry after device Bearer 401.
|
|
54
|
+
*
|
|
55
|
+
* @async
|
|
56
|
+
* @param {string} dataplaneUrl
|
|
57
|
+
* @param {Object} authConfig
|
|
58
|
+
* @param {Object} payload
|
|
59
|
+
* @param {string} systemKey - External system key for credential lookup on retry
|
|
60
|
+
* @returns {Promise<Object>}
|
|
61
|
+
*/
|
|
62
|
+
async function uploadApplicationViaPipelineWithAuthRetry(dataplaneUrl, authConfig, payload, systemKey) {
|
|
63
|
+
const first = await uploadApplicationViaPipeline(dataplaneUrl, authConfig, payload);
|
|
64
|
+
if (!isPipelineAuthFailure(first) || authConfig.type !== 'bearer') {
|
|
65
|
+
return first;
|
|
66
|
+
}
|
|
67
|
+
const clientAuth = await resolveClientTokenAuthForPipelineRetry(systemKey);
|
|
68
|
+
if (!clientAuth || clientAuth.type === 'bearer') {
|
|
69
|
+
return first;
|
|
70
|
+
}
|
|
71
|
+
logger.log(
|
|
72
|
+
chalk.yellow(
|
|
73
|
+
'Device login token was rejected for pipeline publish; retrying with application client token…'
|
|
74
|
+
)
|
|
75
|
+
);
|
|
76
|
+
return uploadApplicationViaPipeline(dataplaneUrl, clientAuth, payload);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = {
|
|
80
|
+
isPipelineAuthFailure,
|
|
81
|
+
uploadApplicationViaPipelineWithAuthRetry
|
|
82
|
+
};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Absolute public base URLs for platform apps (Keycloak, Miso Controller, Dataplane).
|
|
3
|
+
* Used by setup (`platform-controller`), guided footers, and declarative routing.
|
|
4
|
+
*
|
|
5
|
+
* @fileoverview Platform app public URL resolution (Traefik / localhost + dev ports)
|
|
6
|
+
* @author AI Fabrix Team
|
|
7
|
+
* @version 1.0.0
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
'use strict';
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
|
|
15
|
+
const config = require('../core/config');
|
|
16
|
+
const pathsUtil = require('./paths');
|
|
17
|
+
const { loadConfigFile } = require('./config-format');
|
|
18
|
+
const { computePublicUrlBaseString } = require('./url-declarative-public-base');
|
|
19
|
+
const { joinUrlPath, normalizeFrontDoorPatternForHealth } = require('./health-check-url');
|
|
20
|
+
const { getDevPorts } = require('./dev-config');
|
|
21
|
+
|
|
22
|
+
const MISO_CONTROLLER_APP = 'miso-controller';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Bundled `templates/applications/<app>/application.yaml` (fresh install has no builder copy yet).
|
|
26
|
+
*
|
|
27
|
+
* @param {string} appName
|
|
28
|
+
* @returns {Object|null}
|
|
29
|
+
*/
|
|
30
|
+
function loadBundledApplicationVariables(appName) {
|
|
31
|
+
const templateConfigPath = path.join(
|
|
32
|
+
pathsUtil.getProjectRoot(),
|
|
33
|
+
'templates',
|
|
34
|
+
'applications',
|
|
35
|
+
appName,
|
|
36
|
+
'application.yaml'
|
|
37
|
+
);
|
|
38
|
+
if (!fs.existsSync(templateConfigPath)) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const variables = loadConfigFile(templateConfigPath);
|
|
42
|
+
return variables && typeof variables === 'object' ? variables : null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Load platform app variables from materialized builder config, or from bundled template
|
|
47
|
+
* when `aifabrix setup` runs before `up-platform` has copied templates.
|
|
48
|
+
*
|
|
49
|
+
* @param {string} appName
|
|
50
|
+
* @returns {Object}
|
|
51
|
+
*/
|
|
52
|
+
function loadAppVariables(appName) {
|
|
53
|
+
const builderPath = pathsUtil.getBuilderPath(appName);
|
|
54
|
+
if (fs.existsSync(builderPath)) {
|
|
55
|
+
try {
|
|
56
|
+
const configPath = pathsUtil.resolveApplicationConfigPath(builderPath);
|
|
57
|
+
return loadConfigFile(configPath) || {};
|
|
58
|
+
} catch (err) {
|
|
59
|
+
const msg = err && err.message ? String(err.message) : '';
|
|
60
|
+
if (!msg.includes('Application config not found')) {
|
|
61
|
+
throw err;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const fromTemplate = loadBundledApplicationVariables(appName);
|
|
66
|
+
if (fromTemplate) {
|
|
67
|
+
return fromTemplate;
|
|
68
|
+
}
|
|
69
|
+
throw new Error(
|
|
70
|
+
`Application config not found for platform app "${appName}". ` +
|
|
71
|
+
'Run `aifabrix setup` or `aifabrix up-platform` to materialize builder templates.'
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function computeLocalFullPlatformFrontDoorUrl(params) {
|
|
76
|
+
const { setupPlatformMode, remoteServer, developerIdNum, frontDoorRouting } = params;
|
|
77
|
+
if (setupPlatformMode !== 'full') {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
if (remoteServer && String(remoteServer).trim()) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const ports = getDevPorts(Number.isFinite(developerIdNum) ? developerIdNum : 0);
|
|
84
|
+
const mount = normalizeFrontDoorPatternForHealth(frontDoorRouting.pattern);
|
|
85
|
+
// Full local platform uses a single front-door port (3000 + devId×100), not the infra Traefik port.
|
|
86
|
+
return joinUrlPath(`http://localhost:${ports.app}`, mount);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Compute the absolute base URL for a platform app (same rules as guided `up-platform` footer).
|
|
91
|
+
*
|
|
92
|
+
* @async
|
|
93
|
+
* @param {string} appName - e.g. `miso-controller`, `keycloak`, `dataplane`
|
|
94
|
+
* @returns {Promise<string>}
|
|
95
|
+
*/
|
|
96
|
+
async function computeAppBaseUrl(appName) {
|
|
97
|
+
const variables = loadAppVariables(appName);
|
|
98
|
+
const basePort = Number(variables.port || 3000);
|
|
99
|
+
const developerIdRaw = await config.getDeveloperId();
|
|
100
|
+
const developerIdNum =
|
|
101
|
+
typeof developerIdRaw === 'string' ? parseInt(developerIdRaw, 10) : developerIdRaw;
|
|
102
|
+
const userCfg = await config.getConfig();
|
|
103
|
+
const remoteServer = await config.getRemoteServer();
|
|
104
|
+
const infraTlsEnabled = Boolean(userCfg && userCfg.tlsEnabled);
|
|
105
|
+
|
|
106
|
+
const fd = variables.frontDoorRouting || null;
|
|
107
|
+
const frontDoorEnabled = Boolean(fd && fd.enabled === true);
|
|
108
|
+
const traefikOn = Boolean(userCfg && userCfg.traefik);
|
|
109
|
+
const pathActive = Boolean(traefikOn && frontDoorEnabled);
|
|
110
|
+
const setupPlatformMode = await config.getSetupPlatformMode();
|
|
111
|
+
|
|
112
|
+
const publicBase = computePublicUrlBaseString({
|
|
113
|
+
traefik: traefikOn,
|
|
114
|
+
pathActive,
|
|
115
|
+
hostTemplate: fd ? fd.host : null,
|
|
116
|
+
tls: fd ? fd.tls : true,
|
|
117
|
+
developerIdRaw,
|
|
118
|
+
remoteServer,
|
|
119
|
+
profile: 'docker',
|
|
120
|
+
listenPort: basePort,
|
|
121
|
+
developerIdNum,
|
|
122
|
+
infraTlsEnabled
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
if (pathActive) {
|
|
126
|
+
const localFullUrl = computeLocalFullPlatformFrontDoorUrl({
|
|
127
|
+
setupPlatformMode,
|
|
128
|
+
remoteServer,
|
|
129
|
+
developerIdNum,
|
|
130
|
+
frontDoorRouting: fd
|
|
131
|
+
});
|
|
132
|
+
if (localFullUrl) return localFullUrl;
|
|
133
|
+
const mount = normalizeFrontDoorPatternForHealth(fd.pattern);
|
|
134
|
+
return joinUrlPath(publicBase, mount);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return String(publicBase).replace(/\/+$/, '');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Absolute Miso Controller URL for local platform install / setup auth.
|
|
142
|
+
*
|
|
143
|
+
* @async
|
|
144
|
+
* @returns {Promise<string>}
|
|
145
|
+
*/
|
|
146
|
+
async function resolvePlatformControllerUrl() {
|
|
147
|
+
return computeAppBaseUrl(MISO_CONTROLLER_APP);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
module.exports = {
|
|
151
|
+
MISO_CONTROLLER_APP,
|
|
152
|
+
loadAppVariables,
|
|
153
|
+
loadBundledApplicationVariables,
|
|
154
|
+
computeAppBaseUrl,
|
|
155
|
+
resolvePlatformControllerUrl
|
|
156
|
+
};
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Recreate platform Postgres databases and roles after setup "wipe data".
|
|
3
|
+
*
|
|
4
|
+
* Infra init scripts run only on first volume creation; dropping DBs in a
|
|
5
|
+
* running Postgres container requires explicit bootstrap before Keycloak/Miso start.
|
|
6
|
+
*
|
|
7
|
+
* @fileoverview Platform database bootstrap after postgres wipe
|
|
8
|
+
* @author AI Fabrix Team
|
|
9
|
+
* @version 1.0.0
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
const config = require('../core/config');
|
|
15
|
+
const adminSecrets = require('../core/admin-secrets');
|
|
16
|
+
const logger = require('./logger');
|
|
17
|
+
const chalk = require('chalk');
|
|
18
|
+
const { successGlyph } = require('./cli-test-layout-chalk');
|
|
19
|
+
const { getPostgresContainerName } = require('./postgres-wipe');
|
|
20
|
+
const dockerExec = require('./docker-exec');
|
|
21
|
+
const { isSetupQuietOutput } = require('./setup-quiet-output');
|
|
22
|
+
|
|
23
|
+
const SUPERUSER_ROLE = 'pgadmin';
|
|
24
|
+
|
|
25
|
+
/** @type {{ dbName: string, secretKey: string }[]} */
|
|
26
|
+
const PLATFORM_DATABASE_SLOTS = [
|
|
27
|
+
{ dbName: 'keycloak', secretKey: 'databases-keycloak-0-passwordKeyVault' },
|
|
28
|
+
{ dbName: 'miso', secretKey: 'databases-miso-controller-0-passwordKeyVault' },
|
|
29
|
+
{ dbName: 'miso-logs', secretKey: 'databases-miso-controller-1-passwordKeyVault' },
|
|
30
|
+
{ dbName: 'dataplane', secretKey: 'databases-dataplane-0-passwordKeyVault' },
|
|
31
|
+
{ dbName: 'dataplane-vector', secretKey: 'databases-dataplane-1-passwordKeyVault' },
|
|
32
|
+
{ dbName: 'dataplane-logs', secretKey: 'databases-dataplane-2-passwordKeyVault' },
|
|
33
|
+
{ dbName: 'dataplane-records', secretKey: 'databases-dataplane-3-passwordKeyVault' }
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {string} dbName
|
|
38
|
+
* @returns {string}
|
|
39
|
+
*/
|
|
40
|
+
function pgRoleName(dbName) {
|
|
41
|
+
return `${String(dbName).replace(/-/g, '_')}_user`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @param {string} s
|
|
46
|
+
* @returns {string}
|
|
47
|
+
*/
|
|
48
|
+
function escapePgString(s) {
|
|
49
|
+
if (s === null || s === undefined || typeof s !== 'string') return '';
|
|
50
|
+
return s.replace(/'/g, '\'\'');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @param {string} urlOrPassword
|
|
55
|
+
* @returns {string|null}
|
|
56
|
+
*/
|
|
57
|
+
function extractPasswordFromUrlOrValue(urlOrPassword) {
|
|
58
|
+
if (typeof urlOrPassword !== 'string' || urlOrPassword.length === 0) return null;
|
|
59
|
+
if (!urlOrPassword.includes('://')) return urlOrPassword;
|
|
60
|
+
try {
|
|
61
|
+
const u = new URL(
|
|
62
|
+
urlOrPassword.replace(/\$\{DB_HOST\}/g, 'postgres').replace(/\$\{DB_PORT\}/g, '5432')
|
|
63
|
+
);
|
|
64
|
+
return u.password || null;
|
|
65
|
+
} catch {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Escape SQL for embedding in a double-quoted shell argument (-c "...").
|
|
72
|
+
* Bash expands `$` and `$$` (PID) inside double quotes; backslash-escape every `$`.
|
|
73
|
+
*
|
|
74
|
+
* @param {string} s
|
|
75
|
+
* @returns {string}
|
|
76
|
+
*/
|
|
77
|
+
function escapeShellDoubleQuoted(s) {
|
|
78
|
+
return s.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\$/g, '\\$');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @async
|
|
83
|
+
* @param {string} container
|
|
84
|
+
* @param {string} sql
|
|
85
|
+
* @param {string} adminPassword
|
|
86
|
+
* @returns {Promise<void>}
|
|
87
|
+
*/
|
|
88
|
+
async function runPsqlCommand(container, sql, adminPassword, database = 'postgres') {
|
|
89
|
+
const escapedSql = escapeShellDoubleQuoted(sql);
|
|
90
|
+
const cmd =
|
|
91
|
+
`docker exec -e PGPASSWORD -i ${container} psql -U ${SUPERUSER_ROLE} -d ${database} ` +
|
|
92
|
+
`-v ON_ERROR_STOP=1 -c "${escapedSql}"`;
|
|
93
|
+
await dockerExec.execWithDockerEnv(cmd, { env: { PGPASSWORD: adminPassword } });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* @async
|
|
98
|
+
* @param {string} container
|
|
99
|
+
* @param {string} dbName
|
|
100
|
+
* @param {string} role
|
|
101
|
+
* @param {string} password
|
|
102
|
+
* @param {string} adminPassword
|
|
103
|
+
* @returns {Promise<void>}
|
|
104
|
+
*/
|
|
105
|
+
async function ensureRoleAndDatabase(container, dbName, role, password, adminPassword) {
|
|
106
|
+
if (!/^[A-Za-z_][A-Za-z0-9_-]*$/.test(dbName) || !/^[A-Za-z_][A-Za-z0-9_-]*$/.test(role)) {
|
|
107
|
+
throw new Error(`Unsafe database or role name: ${dbName} / ${role}`);
|
|
108
|
+
}
|
|
109
|
+
const pwd = escapePgString(password);
|
|
110
|
+
await runPsqlCommand(
|
|
111
|
+
container,
|
|
112
|
+
`DO $$ BEGIN IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '${role}') THEN CREATE USER "${role}" WITH PASSWORD '${pwd}'; ELSE ALTER USER "${role}" WITH PASSWORD '${pwd}'; END IF; END $$;`,
|
|
113
|
+
adminPassword
|
|
114
|
+
);
|
|
115
|
+
const exists = await dockerExec.execWithDockerEnv(
|
|
116
|
+
`docker exec -e PGPASSWORD -i ${container} psql -U ${SUPERUSER_ROLE} -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = '${dbName}'"`,
|
|
117
|
+
{ env: { PGPASSWORD: adminPassword } }
|
|
118
|
+
);
|
|
119
|
+
const hasDb = String((exists && exists.stdout) || '').trim() === '1';
|
|
120
|
+
if (!hasDb) {
|
|
121
|
+
await runPsqlCommand(
|
|
122
|
+
container,
|
|
123
|
+
`CREATE DATABASE "${dbName}" OWNER "${role}";`,
|
|
124
|
+
adminPassword
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
await runPsqlCommand(
|
|
128
|
+
container,
|
|
129
|
+
`GRANT ALL ON SCHEMA public TO "${role}"; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO "${role}";`,
|
|
130
|
+
adminPassword,
|
|
131
|
+
dbName
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* @async
|
|
137
|
+
* @param {string} container
|
|
138
|
+
* @param {{ dbName: string, secretKey: string }} slot
|
|
139
|
+
* @param {Object} secrets
|
|
140
|
+
* @param {string} adminPassword
|
|
141
|
+
* @returns {Promise<string>}
|
|
142
|
+
*/
|
|
143
|
+
async function ensurePlatformDatabaseSlot(container, slot, secrets, adminPassword) {
|
|
144
|
+
const raw = secrets[slot.secretKey];
|
|
145
|
+
const password = extractPasswordFromUrlOrValue(raw);
|
|
146
|
+
if (!password || !password.trim()) {
|
|
147
|
+
throw new Error(
|
|
148
|
+
`Secret ${slot.secretKey} is missing or empty. Run "aifabrix up-infra" to ensure infra secrets.`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
const role = pgRoleName(slot.dbName);
|
|
152
|
+
await ensureRoleAndDatabase(container, slot.dbName, role, password.trim(), adminPassword);
|
|
153
|
+
if (!isSetupQuietOutput()) {
|
|
154
|
+
logger.log(chalk.gray(` ${successGlyph()} Ensured database ${slot.dbName} (${role})`));
|
|
155
|
+
}
|
|
156
|
+
return slot.dbName;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* @async
|
|
161
|
+
* @returns {Promise<{ container: string, adminPassword: string, secrets: Object }>}
|
|
162
|
+
*/
|
|
163
|
+
async function resolveBootstrapContext() {
|
|
164
|
+
const devId = await config.getDeveloperId();
|
|
165
|
+
const container = getPostgresContainerName(devId);
|
|
166
|
+
const admin = await adminSecrets.readAndDecryptAdminSecrets();
|
|
167
|
+
const adminPassword = admin && admin.POSTGRES_PASSWORD;
|
|
168
|
+
if (!adminPassword) {
|
|
169
|
+
throw new Error('POSTGRES_PASSWORD not found in admin-secrets.env. Run "aifabrix up-infra" first.');
|
|
170
|
+
}
|
|
171
|
+
const secretsEnsure = require('../core/secrets-ensure');
|
|
172
|
+
await secretsEnsure.ensureInfraSecrets();
|
|
173
|
+
const { loadSecrets } = require('../core/secrets');
|
|
174
|
+
const secrets = await loadSecrets(undefined);
|
|
175
|
+
return { container, adminPassword, secrets };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Recreate platform DB users and databases from resolved kv secrets.
|
|
180
|
+
*
|
|
181
|
+
* @async
|
|
182
|
+
* @returns {Promise<string[]>} database names ensured
|
|
183
|
+
*/
|
|
184
|
+
async function bootstrapPlatformPostgresDatabases() {
|
|
185
|
+
const { container, adminPassword, secrets } = await resolveBootstrapContext();
|
|
186
|
+
const ensured = [];
|
|
187
|
+
for (const slot of PLATFORM_DATABASE_SLOTS) {
|
|
188
|
+
ensured.push(await ensurePlatformDatabaseSlot(container, slot, secrets, adminPassword));
|
|
189
|
+
}
|
|
190
|
+
return ensured;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
module.exports = {
|
|
194
|
+
PLATFORM_DATABASE_SLOTS,
|
|
195
|
+
bootstrapPlatformPostgresDatabases,
|
|
196
|
+
pgRoleName,
|
|
197
|
+
extractPasswordFromUrlOrValue,
|
|
198
|
+
escapeShellDoubleQuoted
|
|
199
|
+
};
|
|
@@ -22,6 +22,7 @@ const dockerExec = require('./docker-exec');
|
|
|
22
22
|
const logger = require('./logger');
|
|
23
23
|
const chalk = require('chalk');
|
|
24
24
|
const { successGlyph } = require('./cli-test-layout-chalk');
|
|
25
|
+
const { isSetupQuietOutput } = require('./setup-quiet-output');
|
|
25
26
|
|
|
26
27
|
/** Roles that must never be dropped (Postgres-managed predefined roles). */
|
|
27
28
|
const PROTECTED_ROLES = new Set([
|
|
@@ -190,15 +191,21 @@ async function wipePostgresData() {
|
|
|
190
191
|
}
|
|
191
192
|
|
|
192
193
|
const databases = await listUserDatabases(container, password);
|
|
194
|
+
const quiet = isSetupQuietOutput();
|
|
195
|
+
|
|
193
196
|
for (const dbName of databases) {
|
|
194
197
|
await dropDatabase(container, dbName, password);
|
|
195
|
-
|
|
198
|
+
if (!quiet) {
|
|
199
|
+
logger.log(chalk.gray(` ${successGlyph()} Dropped database ${dbName}`));
|
|
200
|
+
}
|
|
196
201
|
}
|
|
197
202
|
|
|
198
203
|
const roles = await listDropableRoles(container, password);
|
|
199
204
|
for (const role of roles) {
|
|
200
205
|
await dropRole(container, role, password);
|
|
201
|
-
|
|
206
|
+
if (!quiet) {
|
|
207
|
+
logger.log(chalk.gray(` ${successGlyph()} Dropped role ${role}`));
|
|
208
|
+
}
|
|
202
209
|
}
|
|
203
210
|
|
|
204
211
|
return { databases, roles };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chooses `local` vs `docker` for `generateEnvFile` / `resolve` (integration → local, no Docker rewrite).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Resolve environment mode for env generation
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const path = require('path');
|
|
12
|
+
|
|
13
|
+
/** Platform builder apps that run in Docker and need docker host/port transforms on resolve. */
|
|
14
|
+
const PLATFORM_DOCKER_APP_KEYS = new Set(['keycloak', 'miso-controller', 'dataplane']);
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @param {string} appPath - Resolved app directory from getResolveAppPath
|
|
18
|
+
* @returns {boolean}
|
|
19
|
+
*/
|
|
20
|
+
function isIntegrationAppPath(appPath) {
|
|
21
|
+
if (!appPath || typeof appPath !== 'string') return false;
|
|
22
|
+
const normalized = path.resolve(appPath);
|
|
23
|
+
const marker = `${path.sep}integration${path.sep}`;
|
|
24
|
+
return normalized.includes(marker);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {string} appPath - Resolved application directory
|
|
29
|
+
* @param {string} [appName] - App or system key
|
|
30
|
+
* @returns {'local'|'docker'}
|
|
31
|
+
*/
|
|
32
|
+
function resolveGenerateEnvEnvironment(appPath, appName) {
|
|
33
|
+
if (isIntegrationAppPath(appPath)) {
|
|
34
|
+
return 'local';
|
|
35
|
+
}
|
|
36
|
+
const key = String(appName || '').trim().toLowerCase();
|
|
37
|
+
if (PLATFORM_DOCKER_APP_KEYS.has(key)) {
|
|
38
|
+
return 'docker';
|
|
39
|
+
}
|
|
40
|
+
return 'docker';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = {
|
|
44
|
+
PLATFORM_DOCKER_APP_KEYS,
|
|
45
|
+
isIntegrationAppPath,
|
|
46
|
+
resolveGenerateEnvEnvironment
|
|
47
|
+
};
|
|
@@ -77,42 +77,44 @@ function loadYamlTolerantOfDuplicateKeys(content) {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
/**
|
|
80
|
-
*
|
|
81
|
-
* @param {
|
|
80
|
+
* True when secrets map already has a non-empty value for a kv path (flat or nested).
|
|
81
|
+
* @param {Object} secrets
|
|
82
|
+
* @param {string} pathStr - Path after kv:// (e.g. hubspot-demo/token)
|
|
82
83
|
* @returns {boolean}
|
|
83
84
|
*/
|
|
84
|
-
function
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
function secretPathExistsInStore(secrets, pathStr) {
|
|
86
|
+
if (!secrets || typeof secrets !== 'object' || !pathStr) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
const direct = secrets[pathStr];
|
|
90
|
+
if (direct !== undefined && direct !== null && String(direct).trim() !== '') {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
if (!pathStr.includes('/')) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
let current = secrets;
|
|
97
|
+
for (const segment of pathStr.split('/')) {
|
|
98
|
+
if (current === null || current === undefined || typeof current !== 'object' || !(segment in current)) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
current = current[segment];
|
|
102
|
+
}
|
|
103
|
+
return current !== undefined && current !== null && String(current).trim() !== '';
|
|
87
104
|
}
|
|
88
105
|
|
|
89
106
|
/**
|
|
90
|
-
* Finds missing secret keys from template (skips commented and empty lines)
|
|
107
|
+
* Finds missing secret keys from template (skips commented and empty lines).
|
|
108
|
+
* Uses full kv:// paths (e.g. hubspot-demo/token), aligned with collectMissingSecrets.
|
|
91
109
|
* @function findMissingSecretKeys
|
|
92
110
|
* @param {string} envTemplate - Environment template content
|
|
93
111
|
* @param {Object} existingSecrets - Existing secrets object
|
|
94
112
|
* @returns {string[]} Array of missing secret keys
|
|
95
113
|
*/
|
|
96
114
|
function findMissingSecretKeys(envTemplate, existingSecrets) {
|
|
97
|
-
const
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
const lines = envTemplate.split('\n');
|
|
101
|
-
|
|
102
|
-
for (const line of lines) {
|
|
103
|
-
if (isCommentOrEmptyLine(line)) continue;
|
|
104
|
-
let match;
|
|
105
|
-
kvPattern.lastIndex = 0;
|
|
106
|
-
while ((match = kvPattern.exec(line)) !== null) {
|
|
107
|
-
const secretKey = match[1];
|
|
108
|
-
if (!seenKeys.has(secretKey) && !(secretKey in existingSecrets)) {
|
|
109
|
-
missingKeys.push(secretKey);
|
|
110
|
-
seenKeys.add(secretKey);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return missingKeys;
|
|
115
|
+
const { collectUniqueKvPathStrings } = require('./secrets-kv-refs');
|
|
116
|
+
const paths = collectUniqueKvPathStrings(envTemplate);
|
|
117
|
+
return paths.filter(pathStr => !secretPathExistsInStore(existingSecrets, pathStr));
|
|
116
118
|
}
|
|
117
119
|
|
|
118
120
|
/**
|
|
@@ -275,6 +277,8 @@ function loadExistingSecrets(resolvedPath) {
|
|
|
275
277
|
* @throws {Error} If save fails
|
|
276
278
|
*/
|
|
277
279
|
function saveSecretsFile(resolvedPath, secrets) {
|
|
280
|
+
const { assertWritableSecretsPathForTests } = require('./aifabrix-test-runtime-guard');
|
|
281
|
+
assertWritableSecretsPathForTests(resolvedPath);
|
|
278
282
|
const dir = path.dirname(resolvedPath);
|
|
279
283
|
if (!fs.existsSync(dir)) {
|
|
280
284
|
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
@@ -356,6 +360,8 @@ function appendSecretsToFile(resolvedPath, secrets) {
|
|
|
356
360
|
if (!secrets || typeof secrets !== 'object' || Object.keys(secrets).length === 0) {
|
|
357
361
|
return;
|
|
358
362
|
}
|
|
363
|
+
const { assertWritableSecretsPathForTests } = require('./aifabrix-test-runtime-guard');
|
|
364
|
+
assertWritableSecretsPathForTests(resolvedPath);
|
|
359
365
|
const dir = path.dirname(resolvedPath);
|
|
360
366
|
if (!fs.existsSync(dir)) {
|
|
361
367
|
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|