@aifabrix/builder 2.42.1 → 2.44.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/anchor-docs.mdc +15 -0
- package/README.md +2 -2
- package/anchor-docs/README.md +10 -0
- package/anchor-docs/_TEMPLATE +24 -0
- package/bin/aifabrix.js +13 -4
- package/integration/hubspot-test/README.md +157 -0
- package/integration/{hubspot → hubspot-test}/application.json +6 -6
- package/integration/{hubspot → hubspot-test}/create-hubspot.js +10 -10
- package/integration/hubspot-test/env.template +4 -0
- package/integration/hubspot-test/hubspot-test-datasource-company.json +138 -0
- package/integration/hubspot-test/hubspot-test-datasource-contact.json +146 -0
- package/integration/hubspot-test/hubspot-test-datasource-deal.json +146 -0
- package/integration/hubspot-test/hubspot-test-datasource-users.json +76 -0
- package/integration/{hubspot/hubspot-deploy.json → hubspot-test/hubspot-test-deploy.json} +201 -24
- 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-dataplane-down-tests.js +1 -7
- package/integration/{hubspot → hubspot-test}/test-dataplane-down.js +3 -3
- package/integration/{hubspot → hubspot-test}/test.js +137 -102
- package/integration/{hubspot → hubspot-test}/wizard-hubspot-e2e.yaml +2 -2
- package/integration/{hubspot → hubspot-test}/wizard-hubspot-platform.yaml +1 -1
- package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +23 -0
- package/integration/roundtrip-test-local/README.md +144 -0
- package/integration/roundtrip-test-local/application.yaml +13 -0
- package/integration/roundtrip-test-local/env.template +15 -0
- package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +14 -0
- package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +61 -0
- package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +25 -0
- package/integration/roundtrip-test-local2/README.md +144 -0
- package/integration/roundtrip-test-local2/application.yaml +13 -0
- package/integration/roundtrip-test-local2/env.template +15 -0
- package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +31 -0
- package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +86 -0
- package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +25 -0
- package/integration/test/wizard.yaml +8 -0
- package/jest.config.default.js +10 -0
- package/jest.config.integration.fixtures.js +22 -0
- package/jest.config.integration.js +21 -18
- package/jest.config.isolated.js +10 -0
- package/jest.projects.js +288 -0
- package/lib/api/datasources-core.api.js +3 -3
- package/lib/api/dev-mtls-request.js +110 -0
- package/lib/api/dev-server-https.js +145 -0
- package/lib/api/dev.api.js +133 -144
- package/lib/api/index.js +0 -1
- package/lib/api/pipeline.api.js +67 -20
- package/lib/api/service-users.api.js +111 -2
- package/lib/api/types/dev.types.js +4 -3
- package/lib/api/types/pipeline.types.js +8 -5
- package/lib/api/types/service-users.types.js +41 -0
- package/lib/api/types/validation-run.types.js +56 -0
- package/lib/api/validation-run.api.js +99 -0
- package/lib/api/validation-runner.js +99 -0
- package/lib/app/config.js +1 -1
- package/lib/app/deploy-status-display.js +2 -2
- package/lib/app/deploy.js +7 -6
- package/lib/app/display.js +2 -1
- package/lib/app/dockerfile.js +3 -2
- package/lib/app/down.js +2 -1
- package/lib/app/helpers.js +6 -5
- package/lib/app/index.js +27 -8
- package/lib/app/list.js +7 -6
- package/lib/app/push.js +4 -3
- package/lib/app/register.js +19 -8
- package/lib/app/rotate-secret.js +17 -13
- package/lib/app/run-container-start.js +184 -0
- package/lib/app/run-docker-fallback.js +108 -0
- package/lib/app/run-env-compose.js +30 -42
- package/lib/app/run-helpers.js +49 -126
- package/lib/app/run-infra-requirements.js +30 -0
- package/lib/app/run-resolve-image.js +21 -0
- package/lib/app/run.js +74 -21
- package/lib/app/show-display.js +1 -1
- package/lib/app/show.js +1 -1
- package/lib/build/index.js +13 -10
- package/lib/cli/index.js +2 -0
- package/lib/cli/setup-app.help.js +67 -0
- package/lib/cli/setup-app.js +59 -123
- package/lib/cli/setup-app.test-commands.js +179 -0
- package/lib/cli/setup-auth.js +36 -14
- package/lib/cli/setup-credential-deployment.js +22 -8
- package/lib/cli/setup-dev-path-commands.js +124 -0
- package/lib/cli/setup-dev.js +190 -103
- package/lib/cli/setup-environment.js +11 -20
- package/lib/cli/setup-external-system.js +62 -22
- package/lib/cli/setup-infra.js +139 -47
- package/lib/cli/setup-parameters.js +32 -0
- package/lib/cli/setup-secrets.js +147 -10
- package/lib/cli/setup-service-user.js +146 -20
- package/lib/cli/setup-utility.js +47 -19
- package/lib/commands/app-down.js +5 -7
- package/lib/commands/app-install.js +14 -7
- package/lib/commands/app-logs.js +13 -10
- package/lib/commands/app-shell.js +4 -1
- package/lib/commands/app-test.js +25 -19
- package/lib/commands/app.js +22 -10
- package/lib/commands/auth-config.js +10 -14
- package/lib/commands/auth-status.js +4 -3
- package/lib/commands/credential-env.js +4 -3
- package/lib/commands/credential-list.js +5 -4
- package/lib/commands/credential-push.js +4 -3
- package/lib/commands/datasource-unified-test-cli.js +495 -0
- package/lib/commands/datasource-unified-test-cli.options.js +149 -0
- package/lib/commands/datasource-validation-cli.js +129 -0
- package/lib/commands/datasource.js +123 -71
- package/lib/commands/deployment-list.js +6 -5
- package/lib/commands/dev-cli-handlers.js +122 -18
- package/lib/commands/dev-down.js +4 -3
- package/lib/commands/dev-init.js +231 -116
- package/lib/commands/dev-show-display.js +473 -0
- package/lib/commands/login-credentials.js +3 -2
- package/lib/commands/login-device.js +4 -3
- package/lib/commands/login.js +5 -4
- package/lib/commands/logout.js +8 -7
- package/lib/commands/parameters-validate.js +54 -0
- package/lib/commands/repair-datasource.js +314 -68
- package/lib/commands/repair-env-template.js +16 -10
- package/lib/commands/repair-rbac.js +25 -19
- package/lib/commands/repair.js +116 -32
- package/lib/commands/secrets-list.js +23 -12
- package/lib/commands/secrets-remove-all.js +220 -0
- package/lib/commands/secrets-remove.js +22 -13
- package/lib/commands/secrets-set.js +21 -12
- package/lib/commands/secrets-validate.js +20 -7
- package/lib/commands/secure.js +10 -9
- package/lib/commands/service-user.js +243 -13
- package/lib/commands/test-e2e-external.js +27 -1
- package/lib/commands/up-common.js +28 -2
- package/lib/commands/up-dataplane.js +31 -18
- package/lib/commands/up-miso.js +19 -29
- package/lib/commands/upload.js +138 -39
- package/lib/commands/wizard-core-helpers.js +1 -1
- package/lib/commands/wizard-dataplane.js +4 -3
- package/lib/commands/wizard-helpers.js +3 -3
- package/lib/commands/wizard.js +2 -2
- package/lib/core/admin-secrets.js +16 -5
- package/lib/core/audit-logger.js +12 -4
- package/lib/core/config-attach-extensions.js +46 -0
- package/lib/core/config-runtime-paths.js +29 -0
- package/lib/core/config.js +59 -58
- package/lib/core/diff.js +3 -2
- package/lib/core/ensure-encryption-key.js +2 -4
- package/lib/core/secrets-ensure-infra.js +77 -0
- package/lib/core/secrets-ensure.js +120 -64
- package/lib/core/secrets-env-write.js +35 -7
- package/lib/core/secrets-infra-placeholder-sync.js +61 -0
- package/lib/core/secrets.js +228 -42
- package/lib/core/templates-env.js +4 -3
- package/lib/core/templates.js +1 -1
- package/lib/datasource/abac-validator.js +148 -0
- package/lib/datasource/deploy.js +75 -53
- package/lib/datasource/field-reference-validator.js +77 -36
- package/lib/datasource/integration-context.js +63 -0
- package/lib/datasource/list.js +8 -7
- package/lib/datasource/log-viewer.js +252 -0
- package/lib/datasource/resolve-app.js +109 -0
- package/lib/datasource/test-e2e.js +95 -155
- package/lib/datasource/test-integration.js +121 -109
- package/lib/datasource/unified-validation-run-body.js +65 -0
- package/lib/datasource/unified-validation-run-post.js +23 -0
- package/lib/datasource/unified-validation-run-resolve.js +43 -0
- package/lib/datasource/unified-validation-run.js +92 -0
- package/lib/datasource/validate.js +162 -15
- package/lib/deployment/deployer.js +4 -3
- package/lib/deployment/environment.js +7 -6
- package/lib/deployment/push.js +17 -8
- package/lib/external-system/delete.js +4 -3
- package/lib/external-system/deploy.js +131 -53
- package/lib/external-system/download-helpers.js +1 -1
- package/lib/external-system/download.js +7 -6
- package/lib/external-system/generator.js +104 -14
- package/lib/external-system/integration-test-dispatch.js +26 -0
- package/lib/external-system/test-execution.js +5 -1
- package/lib/external-system/test-helpers.js +0 -4
- package/lib/external-system/test-system-level-helpers.js +110 -0
- package/lib/external-system/test-system-level.js +83 -44
- package/lib/external-system/test.js +59 -8
- package/lib/generator/builders.js +23 -11
- package/lib/generator/deploy-manifest-azure-kv.js +81 -0
- package/lib/generator/external-controller-manifest.js +3 -3
- package/lib/generator/external.js +23 -11
- package/lib/generator/helpers.js +71 -12
- package/lib/generator/index.js +8 -4
- package/lib/generator/split-readme.js +12 -7
- package/lib/generator/split-variables.js +2 -1
- package/lib/generator/split.js +46 -11
- package/lib/generator/wizard-readme.js +3 -3
- package/lib/generator/wizard.js +16 -13
- package/lib/infrastructure/compose.js +60 -6
- package/lib/infrastructure/helpers.js +238 -51
- package/lib/infrastructure/index.js +64 -37
- package/lib/infrastructure/services.js +21 -15
- package/lib/internal/fs-real-sync.js +104 -0
- package/lib/internal/node-fs.js +98 -0
- package/lib/parameters/database-secret-values.js +173 -0
- package/lib/parameters/infra-kv-discovery.js +121 -0
- package/lib/parameters/infra-parameter-catalog.js +458 -0
- package/lib/parameters/infra-parameter-validate.js +64 -0
- package/lib/schema/application-schema.json +37 -17
- package/lib/schema/datasource-test-run.schema.json +493 -0
- package/lib/schema/deployment-rules.yaml +102 -63
- package/lib/schema/external-datasource.schema.json +1201 -433
- package/lib/schema/external-system.schema.json +181 -5
- package/lib/schema/flag-map-validation-run.json +31 -0
- package/lib/schema/infra-parameter.schema.json +106 -0
- package/lib/schema/infra.parameter.yaml +421 -0
- package/lib/schema/type/credential-auth-templates.json +40 -0
- package/lib/schema/type/document-storage.json +213 -0
- package/lib/schema/type/message-service.json +123 -0
- package/lib/schema/type/vector-store.json +88 -0
- package/lib/utils/aifabrix-runtime-config-dir.js +132 -0
- package/lib/utils/api-error-handler.js +2 -2
- package/lib/utils/api.js +49 -14
- package/lib/utils/app-config-resolver.js +23 -1
- package/lib/utils/app-register-api.js +3 -2
- package/lib/utils/app-register-auth.js +1 -1
- package/lib/utils/app-register-config.js +4 -4
- package/lib/utils/app-register-display.js +3 -2
- package/lib/utils/app-register-validator.js +3 -2
- package/lib/utils/app-run-containers.js +26 -22
- package/lib/utils/app-scoped-config.js +31 -0
- package/lib/utils/app-service-env-from-builder.js +164 -0
- package/lib/utils/build-copy.js +1 -1
- package/lib/utils/build-helpers.js +20 -20
- package/lib/utils/build-resolve-image.js +165 -0
- package/lib/utils/cli-layout-chalk.js +8 -0
- package/lib/utils/cli-test-layout-chalk.js +267 -0
- package/lib/utils/cli-utils.js +88 -11
- package/lib/utils/compose-db-passwords.js +138 -0
- package/lib/utils/compose-generate-docker-compose.js +216 -0
- package/lib/utils/compose-generator.js +197 -291
- package/lib/utils/compose-miso-env.js +18 -0
- package/lib/utils/compose-traefik-ingress-base.js +158 -0
- package/lib/utils/config-paths.js +209 -6
- package/lib/utils/config-scoped-resources-preference.js +41 -0
- package/lib/utils/controller-deployment-outcome.js +68 -0
- package/lib/utils/credential-display.js +2 -2
- package/lib/utils/credential-secrets-env.js +16 -1
- package/lib/utils/dataplane-pipeline-warning.js +4 -3
- package/lib/utils/datasource-test-run-capability-scope.js +43 -0
- package/lib/utils/datasource-test-run-debug-display.js +137 -0
- package/lib/utils/datasource-test-run-debug-slice.js +93 -0
- package/lib/utils/datasource-test-run-display.js +442 -0
- package/lib/utils/datasource-test-run-exit.js +58 -0
- package/lib/utils/datasource-test-run-legacy-adapter.js +93 -0
- package/lib/utils/datasource-test-run-report-version.js +51 -0
- package/lib/utils/datasource-test-run-schema-sync.js +59 -0
- package/lib/utils/datasource-test-run-tty-log.js +81 -0
- package/lib/utils/datasource-validation-watch.js +266 -0
- package/lib/utils/declarative-url-ports.js +47 -0
- package/lib/utils/derive-env-key-from-client-id.js +41 -0
- package/lib/utils/dev-ca-install.js +185 -23
- package/lib/utils/dev-cert-helper.js +266 -17
- package/lib/utils/dev-hosts-helper.js +307 -0
- package/lib/utils/dev-init-cert-hints.js +37 -0
- package/lib/utils/dev-init-health-messages.js +52 -0
- package/lib/utils/dev-init-resolve.js +86 -0
- package/lib/utils/dev-init-ssh-merge.js +65 -0
- package/lib/utils/dev-ssh-config-helper.js +196 -0
- package/lib/utils/dev-user-groups.js +93 -0
- package/lib/utils/docker-build.js +42 -17
- package/lib/utils/docker-exec.js +28 -0
- package/lib/utils/docker-manifest-public-port.js +116 -0
- package/lib/utils/docker-not-running-hint.js +52 -0
- package/lib/utils/docker.js +98 -11
- package/lib/utils/ensure-dev-certs-for-remote-docker.js +192 -0
- package/lib/utils/env-config-loader.js +10 -91
- package/lib/utils/env-copy.js +19 -10
- package/lib/utils/env-map.js +42 -11
- package/lib/utils/env-template.js +2 -2
- package/lib/utils/environment-scoped-resources.js +144 -0
- package/lib/utils/error-formatter.js +125 -9
- package/lib/utils/error-formatters/http-status-errors.js +6 -5
- package/lib/utils/error-formatters/network-errors.js +2 -1
- package/lib/utils/error-formatters/permission-errors.js +2 -1
- package/lib/utils/error-formatters/validation-errors.js +2 -1
- package/lib/utils/external-env-template.js +180 -0
- package/lib/utils/external-readme.js +8 -1
- package/lib/utils/external-system-display.js +277 -136
- package/lib/utils/external-system-local-test-tty.js +389 -0
- package/lib/utils/external-system-readiness-core.js +377 -0
- package/lib/utils/external-system-readiness-deploy-display.js +270 -0
- package/lib/utils/external-system-readiness-display-internals.js +150 -0
- package/lib/utils/external-system-readiness-display.js +186 -0
- package/lib/utils/external-system-test-helpers.js +24 -6
- package/lib/utils/external-system-validators.js +32 -14
- package/lib/utils/health-check-url.js +119 -0
- package/lib/utils/health-check.js +59 -25
- package/lib/utils/help-builder.js +14 -13
- package/lib/utils/image-version.js +4 -8
- package/lib/utils/infra-containers.js +4 -7
- package/lib/utils/infra-env-defaults.js +162 -0
- package/lib/utils/infra-status-display.js +167 -0
- package/lib/utils/infra-status.js +16 -8
- package/lib/utils/local-secrets.js +29 -7
- package/lib/utils/paths.js +136 -48
- package/lib/utils/port-resolver.js +10 -23
- package/lib/utils/redis-env-scope.js +62 -0
- package/lib/utils/register-aifabrix-shell-env.js +204 -0
- package/lib/utils/remote-builder-validation.js +99 -0
- package/lib/utils/remote-dev-auth.js +117 -21
- package/lib/utils/remote-docker-env.js +67 -15
- package/lib/utils/remote-secrets-loader.js +13 -4
- package/lib/utils/resolve-docker-image-ref.js +124 -0
- package/lib/utils/schema-loader.js +22 -9
- package/lib/utils/secrets-bash-kv.js +25 -0
- package/lib/utils/secrets-generator.js +171 -51
- package/lib/utils/secrets-helpers.js +70 -59
- package/lib/utils/secrets-kv-scope.js +60 -0
- package/lib/utils/secrets-utils.js +35 -37
- package/lib/utils/secrets-validation.js +3 -1
- package/lib/utils/secrets-yaml-preserve.js +109 -0
- package/lib/utils/secure-file-permissions.js +91 -0
- package/lib/utils/ssh-key-helper.js +4 -2
- package/lib/utils/template-helpers.js +2 -2
- package/lib/utils/test-log-writer.js +3 -3
- package/lib/utils/token-manager.js +37 -5
- package/lib/utils/url-declarative-public-base.js +188 -0
- package/lib/utils/url-declarative-resolve-build.js +493 -0
- package/lib/utils/url-declarative-resolve-load-doc.js +51 -0
- package/lib/utils/url-declarative-resolve.js +220 -0
- package/lib/utils/url-declarative-token-parse.js +74 -0
- package/lib/utils/url-declarative-url-flags.js +50 -0
- package/lib/utils/url-declarative-vdir-inactive-env.js +99 -0
- package/lib/utils/url-public-path-prefix.js +34 -0
- package/lib/utils/urls-local-registry.js +220 -0
- package/lib/utils/validation-report-tty-kit.js +77 -0
- package/lib/utils/validation-run-poll.js +89 -0
- package/lib/utils/validation-run-post-retry.js +73 -0
- package/lib/utils/validation-run-request.js +98 -0
- package/lib/utils/variable-transformer.js +21 -4
- package/lib/utils/yaml-preserve.js +78 -1
- package/lib/validation/datasource-warnings.js +56 -0
- package/lib/validation/env-template-auth.js +50 -2
- package/lib/validation/external-manifest-validator.js +35 -7
- package/lib/validation/validate-display.js +37 -31
- package/lib/validation/validate.js +9 -10
- package/lib/validation/validator-unresolved-placeholders.js +98 -0
- package/lib/validation/validator.js +32 -78
- package/lib/validation/wizard-config-validator.js +2 -1
- package/package.json +11 -3
- package/scripts/check-datasource-test-run-schema-sync.js +34 -0
- package/scripts/diagnose-cli.js +150 -0
- package/scripts/install-local.js +304 -55
- package/templates/README.md +15 -2
- package/templates/applications/dataplane/application.yaml +52 -2
- package/templates/applications/dataplane/env.template +80 -18
- package/templates/applications/dataplane/rbac.yaml +8 -0
- package/templates/applications/keycloak/application.yaml +9 -1
- package/templates/applications/keycloak/env.template +15 -6
- package/templates/applications/miso-controller/application.yaml +10 -2
- package/templates/applications/miso-controller/env.template +55 -14
- package/templates/applications/miso-controller/rbac.yaml +5 -0
- package/templates/external-system/README.md.hbs +20 -7
- package/templates/external-system/deploy.js.hbs +5 -5
- package/templates/external-system/env.template.hbs +22 -0
- package/templates/external-system/external-datasource.yaml.hbs +197 -118
- package/templates/infra/compose.yaml.hbs +20 -4
- package/templates/python/docker-compose.hbs +16 -0
- package/templates/typescript/docker-compose.hbs +16 -0
- package/integration/hubspot/README.md +0 -102
- package/integration/hubspot/env.template +0 -4
- package/integration/hubspot/hubspot-datasource-company.json +0 -541
- package/integration/hubspot/hubspot-datasource-contact.json +0 -639
- package/integration/hubspot/hubspot-datasource-deal.json +0 -588
- package/integration/hubspot/hubspot-datasource-users.json +0 -116
- 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/lib/api/external-test.api.js +0 -111
- package/lib/schema/env-config.yaml +0 -43
- /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
|
@@ -16,6 +16,17 @@ const yaml = require('js-yaml');
|
|
|
16
16
|
const crypto = require('crypto');
|
|
17
17
|
const logger = require('./logger');
|
|
18
18
|
const pathsUtil = require('./paths');
|
|
19
|
+
const { mergeFlatSecretsYamlPreservingComments } = require('./secrets-yaml-preserve');
|
|
20
|
+
const {
|
|
21
|
+
generateDatabasePasswordValueForKey,
|
|
22
|
+
generateDatabaseUrlValueForKey,
|
|
23
|
+
parseDatabaseSecretKey
|
|
24
|
+
} = require('../parameters/database-secret-values');
|
|
25
|
+
|
|
26
|
+
/** Lazy require so tests can jest.spyOn getInfraParameterCatalog after module load. */
|
|
27
|
+
function infraParameterCatalogModule() {
|
|
28
|
+
return require('../parameters/infra-parameter-catalog');
|
|
29
|
+
}
|
|
19
30
|
|
|
20
31
|
/**
|
|
21
32
|
* Parse key-value pairs from YAML-like lines (last occurrence wins per key).
|
|
@@ -44,7 +55,9 @@ function parseYamlKeyValueLines(content) {
|
|
|
44
55
|
/**
|
|
45
56
|
* Parse YAML content tolerating duplicate keys (last occurrence wins).
|
|
46
57
|
* Use for secrets files that may have been appended to repeatedly.
|
|
47
|
-
* Tries yaml.load first; on
|
|
58
|
+
* Tries yaml.load first; on duplicate-key errors or other parse failures (e.g. `{}` on line 1
|
|
59
|
+
* then `key: value` lines — invalid multi-document without `---`) falls back to line-by-line
|
|
60
|
+
* parse for flat key: value secrets files.
|
|
48
61
|
*
|
|
49
62
|
* @param {string} content - Raw YAML content
|
|
50
63
|
* @returns {Object} Parsed object (last value wins for duplicate keys)
|
|
@@ -56,13 +69,11 @@ function loadYamlTolerantOfDuplicateKeys(content) {
|
|
|
56
69
|
try {
|
|
57
70
|
const parsed = yaml.load(content);
|
|
58
71
|
return parsed && typeof parsed === 'object' ? parsed : {};
|
|
59
|
-
} catch
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
72
|
+
} catch {
|
|
73
|
+
// Duplicate keys, `{}` + appended YAML (invalid multi-doc), or other bad merges —
|
|
74
|
+
// flat secrets files recover via line parse (last key wins).
|
|
75
|
+
return parseYamlKeyValueLines(content);
|
|
64
76
|
}
|
|
65
|
-
return parseYamlKeyValueLines(content);
|
|
66
77
|
}
|
|
67
78
|
|
|
68
79
|
/**
|
|
@@ -104,18 +115,37 @@ function findMissingSecretKeys(envTemplate, existingSecrets) {
|
|
|
104
115
|
return missingKeys;
|
|
105
116
|
}
|
|
106
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Resolve app directory for database key generation (application.yaml lookup).
|
|
120
|
+
* @param {string} key - Secret key
|
|
121
|
+
* @returns {string|null}
|
|
122
|
+
*/
|
|
123
|
+
function resolveAppDirForDatabaseKey(key) {
|
|
124
|
+
const parsed = parseDatabaseSecretKey(key);
|
|
125
|
+
if (!parsed) return null;
|
|
126
|
+
try {
|
|
127
|
+
const builderPath = pathsUtil.getBuilderPath(parsed.appKey);
|
|
128
|
+
if (fs.existsSync(builderPath)) return builderPath;
|
|
129
|
+
} catch {
|
|
130
|
+
/* ignore */
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
const intPath = pathsUtil.getIntegrationPath(parsed.appKey);
|
|
134
|
+
if (fs.existsSync(intPath)) return intPath;
|
|
135
|
+
} catch {
|
|
136
|
+
/* ignore */
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
|
|
107
141
|
/**
|
|
108
142
|
* Generate database password value for a key (databases-*-passwordKeyVault)
|
|
109
143
|
* @param {string} key - Secret key name
|
|
110
144
|
* @returns {string|null} Password string or null if key does not match
|
|
111
145
|
*/
|
|
112
146
|
function generateDbPasswordValue(key) {
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
const appName = dbPasswordMatch[1];
|
|
116
|
-
if (appName === 'miso-controller') return 'miso_pass123';
|
|
117
|
-
const dbName = appName.replace(/-/g, '_');
|
|
118
|
-
return `${dbName}_pass123`;
|
|
147
|
+
const appDir = resolveAppDirForDatabaseKey(key);
|
|
148
|
+
return generateDatabasePasswordValueForKey(key, appDir);
|
|
119
149
|
}
|
|
120
150
|
|
|
121
151
|
/**
|
|
@@ -124,44 +154,98 @@ function generateDbPasswordValue(key) {
|
|
|
124
154
|
* @returns {string|null} URL string or null if key does not match
|
|
125
155
|
*/
|
|
126
156
|
function generateDbUrlValue(key) {
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
157
|
+
const appDir = resolveAppDirForDatabaseKey(key);
|
|
158
|
+
return generateDatabaseUrlValueForKey(key, appDir);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* @param {string} key - Secret key
|
|
163
|
+
* @param {Record<string, string>|undefined} placeholderContext - Merged infra.parameter.yaml defaults + CLI overrides
|
|
164
|
+
* @returns {string|undefined} Value if catalog handled key; undefined to use legacy rules
|
|
165
|
+
*/
|
|
166
|
+
function tryGenerateSecretValueFromCatalog(key, placeholderContext) {
|
|
167
|
+
let catalogEntry;
|
|
168
|
+
try {
|
|
169
|
+
catalogEntry = infraParameterCatalogModule().getInfraParameterCatalog().findEntryForKey(key);
|
|
170
|
+
} catch (err) {
|
|
171
|
+
logger.warn(`infra parameter catalog unavailable (${err.message}); using legacy generators.`);
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
174
|
+
if (!catalogEntry || !catalogEntry.generator) return undefined;
|
|
175
|
+
const g = catalogEntry.generator.type;
|
|
176
|
+
if (g === 'databasePassword') {
|
|
177
|
+
const v = generateDbPasswordValue(key);
|
|
178
|
+
return v !== null ? v : undefined;
|
|
179
|
+
}
|
|
180
|
+
if (g === 'databaseUrl') {
|
|
181
|
+
const v = generateDbUrlValue(key);
|
|
182
|
+
return v !== null ? v : undefined;
|
|
132
183
|
}
|
|
133
|
-
|
|
134
|
-
|
|
184
|
+
if (
|
|
185
|
+
g === 'randomBytes32' ||
|
|
186
|
+
g === 'randomAlphanumeric' ||
|
|
187
|
+
g === 'password' ||
|
|
188
|
+
g === 'emptyString' ||
|
|
189
|
+
g === 'emptyAllowed' ||
|
|
190
|
+
g === 'literal'
|
|
191
|
+
) {
|
|
192
|
+
return infraParameterCatalogModule().generateValueFromCatalogEntry(key, catalogEntry, crypto, placeholderContext);
|
|
193
|
+
}
|
|
194
|
+
return undefined;
|
|
135
195
|
}
|
|
136
196
|
|
|
137
197
|
/**
|
|
138
|
-
*
|
|
139
|
-
* @
|
|
140
|
-
* @
|
|
141
|
-
* @returns {string} Generated secret value
|
|
198
|
+
* Last-resort generation when infra.parameter.yaml cannot be loaded (e.g. tests, broken install).
|
|
199
|
+
* @param {string} key
|
|
200
|
+
* @returns {string}
|
|
142
201
|
*/
|
|
143
|
-
function
|
|
202
|
+
function legacySecretValueWhenCatalogUnavailable(key) {
|
|
144
203
|
const keyLower = key.toLowerCase();
|
|
145
|
-
|
|
146
204
|
if (keyLower.includes('password')) {
|
|
147
205
|
const dbPassword = generateDbPasswordValue(key);
|
|
148
206
|
if (dbPassword !== null) return dbPassword;
|
|
149
207
|
return crypto.randomBytes(32).toString('base64');
|
|
150
208
|
}
|
|
151
|
-
|
|
152
209
|
if (keyLower.includes('url') || keyLower.includes('uri')) {
|
|
153
210
|
const dbUrl = generateDbUrlValue(key);
|
|
154
211
|
if (dbUrl !== null) return dbUrl;
|
|
155
212
|
return '';
|
|
156
213
|
}
|
|
157
|
-
|
|
158
214
|
if (keyLower.includes('key') || keyLower.includes('secret') || keyLower.includes('token')) {
|
|
159
215
|
return crypto.randomBytes(32).toString('base64');
|
|
160
216
|
}
|
|
161
|
-
|
|
162
217
|
return '';
|
|
163
218
|
}
|
|
164
219
|
|
|
220
|
+
/**
|
|
221
|
+
* @param {string} key - Secret key name
|
|
222
|
+
* @param {Record<string, string>|undefined} placeholderContext - Optional merged catalog defaults + CLI (e.g. up-infra)
|
|
223
|
+
* @returns {string} Generated secret value
|
|
224
|
+
*/
|
|
225
|
+
function generateSecretValue(key, placeholderContext) {
|
|
226
|
+
const fromCatalog = tryGenerateSecretValueFromCatalog(key, placeholderContext);
|
|
227
|
+
if (fromCatalog !== undefined) return fromCatalog;
|
|
228
|
+
|
|
229
|
+
const dbPassword = generateDbPasswordValue(key);
|
|
230
|
+
if (dbPassword !== null) return dbPassword;
|
|
231
|
+
const dbUrl = generateDbUrlValue(key);
|
|
232
|
+
if (dbUrl !== null) return dbUrl;
|
|
233
|
+
|
|
234
|
+
try {
|
|
235
|
+
infraParameterCatalogModule().getInfraParameterCatalog();
|
|
236
|
+
} catch {
|
|
237
|
+
logger.warn(
|
|
238
|
+
`Secret key "${key}": infra parameter catalog unavailable; using legacy name heuristics.`
|
|
239
|
+
);
|
|
240
|
+
return legacySecretValueWhenCatalogUnavailable(key);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
throw new Error(
|
|
244
|
+
`Cannot generate secret for key "${key}": no matching rule in infra.parameter.yaml. ` +
|
|
245
|
+
'Add a catalog entry or run `aifabrix parameters validate` after fixing env.template references.'
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
165
249
|
/**
|
|
166
250
|
* Loads existing secrets from file
|
|
167
251
|
* @function loadExistingSecrets
|
|
@@ -196,9 +280,26 @@ function saveSecretsFile(resolvedPath, secrets) {
|
|
|
196
280
|
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
197
281
|
}
|
|
198
282
|
|
|
283
|
+
if (fs.existsSync(resolvedPath)) {
|
|
284
|
+
try {
|
|
285
|
+
const existing = fs.readFileSync(resolvedPath, 'utf8');
|
|
286
|
+
const mergedContent = mergeFlatSecretsYamlPreservingComments(existing, secrets, {
|
|
287
|
+
yaml,
|
|
288
|
+
dumpOpts: YAML_DUMP_OPTS
|
|
289
|
+
});
|
|
290
|
+
if (mergedContent !== null) {
|
|
291
|
+
fs.writeFileSync(resolvedPath, mergedContent + (mergedContent.endsWith('\n') ? '' : '\n'), { mode: 0o600 });
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
} catch (err) {
|
|
295
|
+
logger.warn(`Could not preserve comments when writing secrets file: ${err.message}; rewriting YAML.`);
|
|
296
|
+
// fall through to dump overwrite
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
199
300
|
const yamlContent = yaml.dump(secrets, {
|
|
200
301
|
indent: 2,
|
|
201
|
-
lineWidth:
|
|
302
|
+
lineWidth: -1,
|
|
202
303
|
noRefs: true,
|
|
203
304
|
sortKeys: false
|
|
204
305
|
});
|
|
@@ -206,7 +307,21 @@ function saveSecretsFile(resolvedPath, secrets) {
|
|
|
206
307
|
fs.writeFileSync(resolvedPath, yamlContent, { mode: 0o600 });
|
|
207
308
|
}
|
|
208
309
|
|
|
209
|
-
const YAML_DUMP_OPTS = { indent: 2, lineWidth:
|
|
310
|
+
const YAML_DUMP_OPTS = { indent: 2, lineWidth: -1, noRefs: true, sortKeys: false };
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* @param {string} resolvedPath
|
|
314
|
+
* @returns {string}
|
|
315
|
+
*/
|
|
316
|
+
function readSecretsFileUtf8OrWarnEmpty(resolvedPath) {
|
|
317
|
+
try {
|
|
318
|
+
const raw = fs.readFileSync(resolvedPath, 'utf8');
|
|
319
|
+
return typeof raw === 'string' ? raw : '';
|
|
320
|
+
} catch (err) {
|
|
321
|
+
logger.warn(`Could not read existing secrets file: ${err.message}; appending new keys only.`);
|
|
322
|
+
return '';
|
|
323
|
+
}
|
|
324
|
+
}
|
|
210
325
|
|
|
211
326
|
/**
|
|
212
327
|
* Merges secret keys into the secrets file (load existing, merge, overwrite file).
|
|
@@ -253,12 +368,13 @@ function appendSecretsToFile(resolvedPath, secrets) {
|
|
|
253
368
|
return;
|
|
254
369
|
}
|
|
255
370
|
|
|
256
|
-
|
|
371
|
+
const existing = readSecretsFileUtf8OrWarnEmpty(resolvedPath);
|
|
257
372
|
try {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
373
|
+
yaml.load(existing);
|
|
374
|
+
} catch {
|
|
375
|
+
const mergedObj = { ...loadExistingSecrets(resolvedPath), ...secrets };
|
|
376
|
+
saveSecretsFile(resolvedPath, mergedObj);
|
|
377
|
+
return;
|
|
262
378
|
}
|
|
263
379
|
const separator = existing.length > 0 && !existing.endsWith('\n') ? '\n' : '';
|
|
264
380
|
fs.writeFileSync(resolvedPath, existing + separator + appendContent, { mode: 0o600 });
|
|
@@ -296,7 +412,7 @@ async function generateMissingSecrets(envTemplate, secretsPath) {
|
|
|
296
412
|
|
|
297
413
|
appendSecretsToFile(resolvedPath, newSecrets);
|
|
298
414
|
|
|
299
|
-
logger.log(
|
|
415
|
+
logger.log(`✔ Generated ${missingKeys.length} missing secret key(s): ${missingKeys.join(', ')}`);
|
|
300
416
|
return missingKeys;
|
|
301
417
|
}
|
|
302
418
|
|
|
@@ -314,6 +430,15 @@ async function generateMissingSecrets(envTemplate, secretsPath) {
|
|
|
314
430
|
* await createDefaultSecrets('~/.aifabrix/secrets.yaml');
|
|
315
431
|
* // Default secrets file is created
|
|
316
432
|
*/
|
|
433
|
+
/** Minimal seed keys for first-time secrets file; values match infra.parameter.yaml generators. */
|
|
434
|
+
const DEFAULT_SECRETS_SEED_KEYS = [
|
|
435
|
+
'postgres-passwordKeyVault',
|
|
436
|
+
'redis-passwordKeyVault',
|
|
437
|
+
'redis-url',
|
|
438
|
+
'keycloak-admin-passwordKeyVault',
|
|
439
|
+
'keycloak-web-server-url'
|
|
440
|
+
];
|
|
441
|
+
|
|
317
442
|
async function createDefaultSecrets(secretsPath) {
|
|
318
443
|
const resolvedPath = secretsPath.startsWith('~')
|
|
319
444
|
? path.join(os.homedir(), secretsPath.slice(1))
|
|
@@ -324,22 +449,17 @@ async function createDefaultSecrets(secretsPath) {
|
|
|
324
449
|
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
325
450
|
}
|
|
326
451
|
|
|
327
|
-
const
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
postgres-passwordKeyVault: "admin123"
|
|
332
|
-
|
|
333
|
-
# Redis Secrets
|
|
334
|
-
redis-passwordKeyVault: ""
|
|
335
|
-
redis-url: "redis://\${REDIS_HOST}:\${REDIS_PORT}"
|
|
452
|
+
const seeded = {};
|
|
453
|
+
for (const k of DEFAULT_SECRETS_SEED_KEYS) {
|
|
454
|
+
seeded[k] = generateSecretValue(k);
|
|
455
|
+
}
|
|
336
456
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
457
|
+
const header =
|
|
458
|
+
'# Local development secrets (catalog-aligned seed). Run aifabrix up-infra and aifabrix resolve <app> --force for full keys.\n' +
|
|
459
|
+
'# Production uses Azure Key Vault.\n\n';
|
|
460
|
+
const body = yaml.dump(seeded, { indent: 2, lineWidth: -1, noRefs: true, sortKeys: false });
|
|
341
461
|
|
|
342
|
-
fs.writeFileSync(resolvedPath,
|
|
462
|
+
fs.writeFileSync(resolvedPath, header + body, { mode: 0o600 });
|
|
343
463
|
}
|
|
344
464
|
|
|
345
465
|
module.exports = {
|
|
@@ -46,6 +46,41 @@ function isCommentOrEmptyLine(line) {
|
|
|
46
46
|
/** Regex for kv:// path (allows slashes, e.g. kv://hubspot/clientId) */
|
|
47
47
|
const KV_REF_PATTERN = /kv:\/\/([a-zA-Z0-9_\-/]+)/g;
|
|
48
48
|
|
|
49
|
+
const { resolveBashKvFromProcessEnv } = require('./secrets-bash-kv');
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Last-resort when infra.parameter.yaml cannot be read (e.g. Jest suites that mock `fs`;
|
|
53
|
+
* catalog uses `node:fs`, which is often the same mocked instance). Must stay in sync with
|
|
54
|
+
* `generator.type: emptyAllowed` keys in lib/schema/infra.parameter.yaml.
|
|
55
|
+
*/
|
|
56
|
+
const EMPTY_ALLOWED_KV_FALLBACK = new Set(['redis-passwordKeyVault']);
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Infra catalog keys with generator `emptyAllowed` may be absent from the secrets file;
|
|
60
|
+
* they resolve like an explicit empty string (e.g. local Redis has no password).
|
|
61
|
+
* @param {string} pathStr - kv path segment (flat key or path with slashes)
|
|
62
|
+
* @returns {boolean}
|
|
63
|
+
*/
|
|
64
|
+
function isKvKeyAllowedEmptyWhenAbsent(pathStr) {
|
|
65
|
+
if (!pathStr || typeof pathStr !== 'string') return false;
|
|
66
|
+
try {
|
|
67
|
+
const { getInfraParameterCatalog } = require('../parameters/infra-parameter-catalog');
|
|
68
|
+
return getInfraParameterCatalog().isKeyAllowedEmpty(pathStr);
|
|
69
|
+
} catch {
|
|
70
|
+
try {
|
|
71
|
+
const {
|
|
72
|
+
readRelaxedEmptyAllowedKeySet,
|
|
73
|
+
DEFAULT_CATALOG_PATH
|
|
74
|
+
} = require('../parameters/infra-parameter-catalog');
|
|
75
|
+
const set = readRelaxedEmptyAllowedKeySet(DEFAULT_CATALOG_PATH);
|
|
76
|
+
if (set && set.has(pathStr)) return true;
|
|
77
|
+
} catch {
|
|
78
|
+
/* ignore */
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return EMPTY_ALLOWED_KV_FALLBACK.has(pathStr);
|
|
82
|
+
}
|
|
83
|
+
|
|
49
84
|
/**
|
|
50
85
|
* Find object key that matches part case-insensitively.
|
|
51
86
|
* @param {Object} obj - Object to search
|
|
@@ -98,19 +133,30 @@ function getValueByPath(secrets, pathStr) {
|
|
|
98
133
|
return getValueByNestedPath(secrets, pathStr.split('/'));
|
|
99
134
|
}
|
|
100
135
|
|
|
136
|
+
const { getValueByPathWithEnvScope, mergeSecretsWithPrefixedCopies } =
|
|
137
|
+
require('./secrets-kv-scope').createScopedKvHelpers(getValueByPath);
|
|
138
|
+
|
|
139
|
+
function resolveKvRefValue(secretsMap, pathStr, envKey, effective) {
|
|
140
|
+
const v = getValueByPathWithEnvScope(secretsMap, pathStr, envKey, effective);
|
|
141
|
+
if (v !== undefined && v !== null) return v;
|
|
142
|
+
return resolveBashKvFromProcessEnv(pathStr);
|
|
143
|
+
}
|
|
144
|
+
|
|
101
145
|
/**
|
|
102
146
|
* Collect missing kv:// secrets referenced in content (skips commented and empty lines).
|
|
103
147
|
* Supports path-style refs (e.g. kv://hubspot/clientId). Returns unique refs.
|
|
104
148
|
* @function collectMissingSecrets
|
|
105
149
|
* @param {string} content - Text content
|
|
106
150
|
* @param {Object} secrets - Available secrets (flat or nested)
|
|
151
|
+
* @param {{ effective?: boolean, envKey?: string }|null} [scopedKv] - Optional env-scoped kv resolution
|
|
107
152
|
* @returns {string[]} Array of missing kv://<path> references (unique)
|
|
108
153
|
*/
|
|
109
|
-
function collectMissingSecrets(content, secrets) {
|
|
154
|
+
function collectMissingSecrets(content, secrets, scopedKv = null) {
|
|
155
|
+
const effective = Boolean(scopedKv && scopedKv.effective && scopedKv.envKey);
|
|
156
|
+
const secretsMap = effective ? mergeSecretsWithPrefixedCopies(secrets, scopedKv.envKey) : secrets;
|
|
110
157
|
const seen = new Set();
|
|
111
158
|
const missing = [];
|
|
112
|
-
const
|
|
113
|
-
for (const line of lines) {
|
|
159
|
+
for (const line of content.split('\n')) {
|
|
114
160
|
if (isCommentOrEmptyLine(line)) continue;
|
|
115
161
|
let match;
|
|
116
162
|
KV_REF_PATTERN.lastIndex = 0;
|
|
@@ -118,8 +164,8 @@ function collectMissingSecrets(content, secrets) {
|
|
|
118
164
|
const pathStr = match[1];
|
|
119
165
|
if (seen.has(pathStr)) continue;
|
|
120
166
|
seen.add(pathStr);
|
|
121
|
-
const value =
|
|
122
|
-
if (value === undefined || value === null) {
|
|
167
|
+
const value = resolveKvRefValue(secretsMap, pathStr, scopedKv?.envKey, effective);
|
|
168
|
+
if ((value === undefined || value === null) && !isKvKeyAllowedEmptyWhenAbsent(pathStr)) {
|
|
123
169
|
missing.push(`kv://${pathStr}`);
|
|
124
170
|
}
|
|
125
171
|
}
|
|
@@ -157,14 +203,20 @@ function formatMissingSecretsFileInfo(secretsFilePaths) {
|
|
|
157
203
|
* @param {string} content - Text content containing kv:// references
|
|
158
204
|
* @param {Object} secrets - Secrets map (flat or nested)
|
|
159
205
|
* @param {Object} envVars - Environment variables map for nested interpolation
|
|
206
|
+
* @param {{ effective?: boolean, envKey?: string }|null} [scopedKv] - Optional env-scoped kv resolution
|
|
160
207
|
* @returns {string} Content with kv:// references replaced
|
|
161
208
|
*/
|
|
162
|
-
function replaceKvInContent(content, secrets, envVars) {
|
|
209
|
+
function replaceKvInContent(content, secrets, envVars, scopedKv = null) {
|
|
210
|
+
const effective = Boolean(scopedKv && scopedKv.effective && scopedKv.envKey);
|
|
211
|
+
const secretsMap = effective ? mergeSecretsWithPrefixedCopies(secrets, scopedKv.envKey) : secrets;
|
|
163
212
|
const lines = content.split('\n');
|
|
164
213
|
const result = lines.map(line => {
|
|
165
214
|
if (isCommentOrEmptyLine(line)) return line;
|
|
166
215
|
return line.replace(KV_REF_PATTERN, (match, pathStr) => {
|
|
167
|
-
let value =
|
|
216
|
+
let value = resolveKvRefValue(secretsMap, pathStr, scopedKv?.envKey, effective);
|
|
217
|
+
if ((value === undefined || value === null) && isKvKeyAllowedEmptyWhenAbsent(pathStr)) {
|
|
218
|
+
value = '';
|
|
219
|
+
}
|
|
168
220
|
if (typeof value === 'string') {
|
|
169
221
|
value = value.replace(/\$\{([A-Z_]+)\}/g, (m, envVar) => {
|
|
170
222
|
return envVars[envVar] || m;
|
|
@@ -247,44 +299,23 @@ function getPortFromEnvContent(envContent) {
|
|
|
247
299
|
}
|
|
248
300
|
|
|
249
301
|
/**
|
|
250
|
-
*
|
|
251
|
-
* @function applyDeveloperIdAdjustment
|
|
252
|
-
* @param {number} baseAppPort - Base application port
|
|
253
|
-
* @param {number} devIdNum - Developer ID number
|
|
254
|
-
* @returns {number} Adjusted port
|
|
255
|
-
*/
|
|
256
|
-
function applyDeveloperIdAdjustment(baseAppPort, devIdNum) {
|
|
257
|
-
return devIdNum === 0 ? baseAppPort : (baseAppPort + (devIdNum * 100));
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Calculate application port following override chain and developer-id adjustment
|
|
262
|
-
* Override chain: env-config.yaml → config.yaml → application.yaml port
|
|
302
|
+
* Resolve manifest listen port (container port) for the app from override chain.
|
|
263
303
|
* @async
|
|
264
|
-
* @function calculateAppPort
|
|
265
304
|
* @param {string} [variablesPath] - Path to application config
|
|
266
|
-
* @param {Object} localEnv -
|
|
305
|
+
* @param {Object} localEnv - Merged local env from defaults + config.yaml
|
|
267
306
|
* @param {string} envContent - Environment content for fallback
|
|
268
|
-
* @
|
|
269
|
-
* @returns {Promise<number>} Final application port with developer-id adjustment
|
|
307
|
+
* @returns {Promise<number>} Base listen port (before +10 / +dev*100 local host math)
|
|
270
308
|
*/
|
|
271
|
-
async function
|
|
272
|
-
// Start with env-config value
|
|
309
|
+
async function resolveManifestListenPort(variablesPath, localEnv, envContent) {
|
|
273
310
|
let baseAppPort = getPortFromLocalEnv(localEnv);
|
|
274
|
-
|
|
275
|
-
// Override with application config port (strongest)
|
|
276
311
|
const variablesPort = getPortFromVariablesFile(variablesPath);
|
|
277
312
|
if (variablesPort !== null) {
|
|
278
313
|
baseAppPort = variablesPort;
|
|
279
314
|
}
|
|
280
|
-
|
|
281
|
-
// Fallback to env content if still no port
|
|
282
315
|
if (baseAppPort === null || baseAppPort === undefined) {
|
|
283
316
|
baseAppPort = getPortFromEnvContent(envContent);
|
|
284
317
|
}
|
|
285
|
-
|
|
286
|
-
// Apply developer-id adjustment
|
|
287
|
-
return applyDeveloperIdAdjustment(baseAppPort, devIdNum);
|
|
318
|
+
return baseAppPort;
|
|
288
319
|
}
|
|
289
320
|
|
|
290
321
|
/**
|
|
@@ -325,16 +356,6 @@ function getPortVarFromEnvTemplatePath(variablesPath) {
|
|
|
325
356
|
}
|
|
326
357
|
}
|
|
327
358
|
|
|
328
|
-
/**
|
|
329
|
-
* Adjust infra-related ports in resolved .env content for local environment.
|
|
330
|
-
* Own case: when we generate .env for envOutputPath (not reload), we use localPort (application.yaml build.localPort or port).
|
|
331
|
-
* Sets PORT and the template port var (e.g. MISO_PORT) to localPort so the generated .env is correct for local use.
|
|
332
|
-
* @async
|
|
333
|
-
* @function adjustLocalEnvPortsInContent
|
|
334
|
-
* @param {string} envContent - Resolved .env content
|
|
335
|
-
* @param {string} [variablesPath] - Path to application config (to read port and template port var)
|
|
336
|
-
* @returns {Promise<string>} Updated content with local ports
|
|
337
|
-
*/
|
|
338
359
|
/**
|
|
339
360
|
* Gets developer ID number
|
|
340
361
|
* @async
|
|
@@ -418,12 +439,13 @@ async function buildEnvVarsForInterpolation(devIdNum) {
|
|
|
418
439
|
async function adjustLocalEnvPortsInContent(envContent, variablesPath) {
|
|
419
440
|
const devIdNum = await getDeveloperIdNumber();
|
|
420
441
|
const localEnv = await getLocalEnvWithOverrides();
|
|
442
|
+
const { localHostPort } = require('./declarative-url-ports');
|
|
421
443
|
|
|
422
|
-
const
|
|
423
|
-
const appPort =
|
|
444
|
+
const baseListen = await resolveManifestListenPort(variablesPath, localEnv, envContent);
|
|
445
|
+
const appPort = localHostPort(baseListen, devIdNum);
|
|
424
446
|
|
|
425
447
|
let updated = updatePortVariable(envContent, appPort);
|
|
426
|
-
updated = updateLocalhostUrls(updated,
|
|
448
|
+
updated = updateLocalhostUrls(updated, baseListen, appPort);
|
|
427
449
|
updated = await rewriteInfraEndpoints(updated, 'local');
|
|
428
450
|
|
|
429
451
|
const envVars = await buildEnvVarsForInterpolation(devIdNum);
|
|
@@ -437,18 +459,6 @@ async function adjustLocalEnvPortsInContent(envContent, variablesPath) {
|
|
|
437
459
|
return updated;
|
|
438
460
|
}
|
|
439
461
|
|
|
440
|
-
/**
|
|
441
|
-
* Ensure secrets map is non-empty or throw a friendly guidance error
|
|
442
|
-
* @function ensureNonEmptySecrets
|
|
443
|
-
* @param {Object} secrets - Secrets map
|
|
444
|
-
* @throws {Error} If secrets is empty
|
|
445
|
-
*/
|
|
446
|
-
function ensureNonEmptySecrets(secrets) {
|
|
447
|
-
if (Object.keys(secrets || {}).length === 0) {
|
|
448
|
-
throw new Error('No secrets file found. Please create ~/.aifabrix/secrets.local.yaml or configure aifabrix-secrets in config.yaml');
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
|
|
452
462
|
/**
|
|
453
463
|
* Validate secrets against the env template (skips commented and empty lines)
|
|
454
464
|
* @function validateSecrets
|
|
@@ -465,6 +475,8 @@ module.exports = {
|
|
|
465
475
|
loadEnvConfig,
|
|
466
476
|
interpolateEnvVars,
|
|
467
477
|
collectMissingSecrets,
|
|
478
|
+
resolveBashKvFromProcessEnv,
|
|
479
|
+
mergeSecretsWithPrefixedCopies,
|
|
468
480
|
formatMissingSecretsFileInfo,
|
|
469
481
|
replaceKvInContent,
|
|
470
482
|
resolveServicePortsInEnvContent,
|
|
@@ -473,7 +485,6 @@ module.exports = {
|
|
|
473
485
|
adjustLocalEnvPortsInContent,
|
|
474
486
|
readYamlAtPath,
|
|
475
487
|
applyCanonicalSecretsOverride,
|
|
476
|
-
ensureNonEmptySecrets,
|
|
477
488
|
validateSecrets,
|
|
478
489
|
rewriteInfraEndpoints,
|
|
479
490
|
getEnvHosts
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Env-scoped kv:// resolution helpers (prefixed keys + in-memory aliases).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Split from secrets-helpers for file size limits
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {Function} getValueByPath - From secrets-helpers
|
|
13
|
+
* @returns {{ getValueByPathWithEnvScope: Function, mergeSecretsWithPrefixedCopies: Function }}
|
|
14
|
+
*/
|
|
15
|
+
function createScopedKvHelpers(getValueByPath) {
|
|
16
|
+
/**
|
|
17
|
+
* @param {Object} secrets
|
|
18
|
+
* @param {string} pathStr
|
|
19
|
+
* @param {string|null|undefined} envKey
|
|
20
|
+
* @param {boolean} effective
|
|
21
|
+
* @returns {*}
|
|
22
|
+
*/
|
|
23
|
+
function getValueByPathWithEnvScope(secrets, pathStr, envKey, effective) {
|
|
24
|
+
if (!effective || !envKey) {
|
|
25
|
+
return getValueByPath(secrets, pathStr);
|
|
26
|
+
}
|
|
27
|
+
const prefix = `${String(envKey).toLowerCase()}-`;
|
|
28
|
+
const prefixedPath = prefix + pathStr;
|
|
29
|
+
const fromPrefixed = getValueByPath(secrets, prefixedPath);
|
|
30
|
+
if (fromPrefixed !== undefined && fromPrefixed !== null) {
|
|
31
|
+
return fromPrefixed;
|
|
32
|
+
}
|
|
33
|
+
return getValueByPath(secrets, pathStr);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {Object} secrets
|
|
38
|
+
* @param {string} envKey
|
|
39
|
+
* @returns {Object}
|
|
40
|
+
*/
|
|
41
|
+
function mergeSecretsWithPrefixedCopies(secrets, envKey) {
|
|
42
|
+
if (!secrets || typeof secrets !== 'object' || !envKey) {
|
|
43
|
+
return secrets;
|
|
44
|
+
}
|
|
45
|
+
const prefix = `${String(envKey).toLowerCase()}-`;
|
|
46
|
+
const merged = { ...secrets };
|
|
47
|
+
for (const [k, v] of Object.entries(secrets)) {
|
|
48
|
+
if (typeof k !== 'string' || !k || k.startsWith(prefix)) continue;
|
|
49
|
+
const pk = prefix + k;
|
|
50
|
+
if (merged[pk] === undefined && v !== undefined && v !== null) {
|
|
51
|
+
merged[pk] = v;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return merged;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return { getValueByPathWithEnvScope, mergeSecretsWithPrefixedCopies };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
module.exports = { createScopedKvHelpers };
|