@aifabrix/builder 2.46.0 → 2.47.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.npmrc.token +1 -0
- package/.temp/jest-protection-convert/run-IFN2nv/.protection/hubspot-companies.json +1 -0
- package/.temp/jest-protection-convert/run-IvmKLO/.protection/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-convert/run-SdvblF/.protection/hubspot-companies.json +1 -0
- package/.temp/jest-protection-convert/run-b51TsW/.protection/hubspot-companies.json +1 -0
- package/.temp/jest-protection-convert/run-fiLmb1/.protection/hubspot-companies.json +1 -0
- package/.temp/jest-protection-convert/run-qYGHMU/.protection/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-Mq5re8/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-f3u4YL/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-iQtd1O/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-mdmz5M/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-qbKYxQ/hubspot-companies.yaml +17 -0
- package/.temp/jest-protection-fixtures/hubspot-yzlJy6/hubspot-companies.yaml +17 -0
- package/.temp/validation/summary.txt +5 -0
- package/jest.isolated-projects.js +18 -2
- package/jest.projects.js +24 -0
- package/lib/api/dataplane-health.api.js +19 -7
- package/lib/api/lifecycle.api.js +22 -1
- package/lib/api/resource-types.api.js +107 -0
- package/lib/api/runtime-configuration.api.js +72 -0
- package/lib/api/types/resource-types.types.js +45 -0
- package/lib/api/user-groups.api.js +9 -9
- package/lib/api/users.api.js +8 -8
- package/lib/api/wizard.api.js +10 -2
- package/lib/app/down.js +58 -26
- package/lib/app/index.js +2 -2
- package/lib/app/run-resolve-image.js +2 -1
- package/lib/app/run-stop-containers.js +113 -0
- package/lib/app/run.js +1 -31
- package/lib/app/show-display.js +35 -0
- package/lib/app/show.js +32 -1
- package/lib/build/index.js +17 -5
- package/lib/build/miso-controller-npm-sdk-guard.js +25 -0
- package/lib/channels/add-channel.js +348 -0
- package/lib/channels/channel-artifact-generator.js +141 -0
- package/lib/channels/channel-audit.js +101 -0
- package/lib/channels/channel-auth-context.js +37 -0
- package/lib/channels/channel-credential.js +200 -0
- package/lib/channels/channel-display-name.js +64 -0
- package/lib/channels/channel-external-system.js +106 -0
- package/lib/channels/channel-integration-generator.js +274 -0
- package/lib/channels/channel-probe.js +89 -0
- package/lib/channels/channel-runtime-resolver.js +152 -0
- package/lib/channels/channel-status-display.js +141 -0
- package/lib/channels/channel-targets.js +115 -0
- package/lib/cli/index.js +18 -4
- package/lib/cli/infra-guided-auth.js +36 -12
- package/lib/cli/setup-app.js +54 -3
- package/lib/cli/setup-infra-up-dataplane-action.js +2 -2
- package/lib/cli/setup-infra-up-platform-action.js +2 -1
- package/lib/cli/setup-npm.js +59 -0
- package/lib/cli/setup-platform.js +1 -1
- package/lib/cli/setup-secrets.js +1 -1
- package/lib/cli/setup-utility-help-after.js +4 -1
- package/lib/cli/setup-utility-resolve.js +8 -5
- package/lib/commands/app-logs.js +54 -26
- package/lib/commands/app.js +41 -0
- package/lib/commands/auth-status-dataplane-version.js +97 -61
- package/lib/commands/auth-status.js +2 -7
- package/lib/commands/channel.js +58 -0
- package/lib/commands/datasource.js +1 -1
- package/lib/commands/identity-apply-sync.js +49 -8
- package/lib/commands/identity-group-cli.js +66 -26
- package/lib/commands/identity-membership-role-cache.js +58 -37
- package/lib/commands/identity-shared.js +22 -1
- package/lib/commands/identity-user-cli.js +80 -34
- package/lib/commands/login-device.js +11 -3
- package/lib/commands/login.js +8 -3
- package/lib/commands/npm-config.js +103 -0
- package/lib/commands/repair-auth-apply.js +30 -4
- package/lib/commands/repair-auth-config.js +8 -13
- package/lib/commands/repair-datasource-com-v2.js +15 -0
- package/lib/commands/repair-datasource-service-contract.js +206 -0
- package/lib/commands/repair-datasource.js +53 -18
- package/lib/commands/repair-env-template.js +12 -12
- package/lib/commands/repair-openapi-sync.js +26 -4
- package/lib/commands/repair-steps.js +28 -13
- package/lib/commands/repair-system-alignment.js +7 -4
- package/lib/commands/resource-type.js +371 -0
- package/lib/commands/setup-modes.js +1 -1
- package/lib/commands/setup-prompts-platform-mode.js +24 -21
- package/lib/commands/test-trust-external.js +39 -90
- package/lib/commands/up-common.js +33 -1
- package/lib/commands/up-dataplane.js +8 -4
- package/lib/commands/upload.js +24 -3
- package/lib/commands/verify-trust-command-action.js +4 -1
- package/lib/commands/verify-trust-external.js +12 -20
- package/lib/core/config-setup-platform-mode.js +3 -3
- package/lib/core/setup-platform-mode.js +41 -6
- package/lib/datasource/capability/capability-diff-slice.js +2 -2
- package/lib/datasource/capability/capability-manifest.js +254 -0
- package/lib/datasource/capability/capability-resolve.js +3 -4
- package/lib/datasource/capability/copy-operations.js +5 -3
- package/lib/datasource/capability/remove-operations.js +2 -1
- package/lib/datasource/capability/run-capability-copy.js +32 -4
- package/lib/datasource/capability/run-capability-diff.js +3 -3
- package/lib/datasource/capability/run-capability-dimension.js +2 -4
- package/lib/datasource/capability/run-capability-edit.js +4 -7
- package/lib/datasource/capability/run-capability-relate.js +2 -4
- package/lib/datasource/capability/run-capability-remove.js +2 -4
- package/lib/datasource/capability/validate-capability-slice.js +2 -1
- package/lib/datasource/datasource-identifier.js +2 -1
- package/lib/datasource/datasource-validate-display.js +1 -1
- package/lib/datasource/field-reference-validator.js +24 -3
- package/lib/datasource/orphan-profiles-validator.js +98 -0
- package/lib/datasource/validate.js +41 -23
- package/lib/external-system/download-helpers.js +87 -0
- package/lib/external-system/download.js +61 -122
- package/lib/external-system/generator.js +1 -59
- package/lib/external-system/test.js +10 -15
- package/lib/generator/builders.js +11 -1
- package/lib/generator/external-controller-manifest.js +5 -2
- package/lib/generator/split.js +3 -1
- package/lib/generator/wizard.js +1 -28
- package/lib/identity/fk-reference.js +65 -0
- package/lib/identity/identity-apply-core.js +13 -58
- package/lib/identity/identity-apply-phases.js +5 -1
- package/lib/identity/identity-display.js +243 -0
- package/lib/identity/identity-response-helpers.js +52 -0
- package/lib/identity/identity-sync-errors.js +62 -0
- package/lib/identity/resolve-user-id.js +129 -0
- package/lib/identity/username-default.js +31 -0
- package/lib/lifecycle/certification-progress.js +2 -2
- package/lib/lifecycle/report-display-trust.js +70 -0
- package/lib/lifecycle/report-display.js +14 -13
- package/lib/resolvers/datasource-resolver.js +2 -3
- package/lib/resolvers/resource-type-file.js +159 -0
- package/lib/schema/application-schema.json +16 -0
- package/lib/schema/deployment-rules.yaml +2 -1
- package/lib/schema/external-datasource.schema.json +372 -58
- package/lib/schema/external-system.schema.json +331 -2
- package/lib/schema/type/document-storage.json +9 -5
- package/lib/utils/api.js +2 -1
- package/lib/utils/app-run-containers.js +25 -2
- package/lib/utils/authentication-shape.js +224 -0
- package/lib/utils/build-copy.js +36 -5
- package/lib/utils/build-resolve-image.js +66 -12
- package/lib/utils/celery-companion-apps.js +53 -0
- package/lib/utils/cli-utils.js +4 -0
- package/lib/utils/compose-celery-worker.js +23 -0
- package/lib/utils/compose-generate-docker-compose.js +9 -1
- package/lib/utils/configuration-env-resolver.js +36 -0
- package/lib/utils/controller-url.js +4 -1
- package/lib/utils/credential-secrets-env-kv.js +216 -0
- package/lib/utils/credential-secrets-env.js +52 -262
- package/lib/utils/dataplane-resolver.js +4 -115
- package/lib/utils/declarative-url-ports.js +24 -1
- package/lib/utils/dev-platform-tls.js +148 -0
- package/lib/utils/docker-build.js +1 -4
- package/lib/utils/external-env-template.js +10 -3
- package/lib/utils/external-system-validators.js +8 -2
- package/lib/utils/health-check-request.js +114 -0
- package/lib/utils/health-check-url.js +3 -1
- package/lib/utils/health-check.js +1 -113
- package/lib/utils/help-builder.js +3 -0
- package/lib/utils/keycloak-realm-ready.js +5 -1
- package/lib/utils/localhost-public-host.js +31 -0
- package/lib/utils/npm-user-config.js +145 -0
- package/lib/utils/paths.js +25 -0
- package/lib/utils/platform-controller-url.js +58 -4
- package/lib/utils/platform-kv-ref.js +57 -0
- package/lib/utils/runtime-configuration-download.js +189 -0
- package/lib/utils/runtime-configuration-values.js +119 -0
- package/lib/utils/schema-loader.js +19 -4
- package/lib/utils/url-declarative-public-base.js +33 -25
- package/lib/utils/worker-sidecars.js +142 -0
- package/lib/validation/external-manifest-validator.js +2 -0
- package/lib/validation/runtime-configuration-rules.js +98 -0
- package/lib/validation/validate-display-dimensions.js +154 -0
- package/lib/validation/validate-display-sections.js +276 -0
- package/lib/validation/validate-display-steps.js +67 -0
- package/lib/validation/validate-display.js +24 -402
- package/package.json +1 -1
- package/templates/applications/dataplane/application.yaml +5 -0
- package/templates/applications/dataplane/env.template +26 -13
- package/templates/applications/dataplane/rbac.yaml +108 -1
- package/templates/applications/miso-controller/application.yaml +2 -2
- package/templates/applications/miso-controller/env.template +37 -122
- package/templates/applications/miso-controller/rbac.yaml +28 -13
- package/templates/channels/chatgpt/install.template.md.hbs +49 -0
- package/templates/channels/chatgpt/manifest.template.json.hbs +9 -0
- package/templates/channels/microsoft-copilot/install.template.md.hbs +49 -0
- package/templates/channels/microsoft-copilot/manifest.template.json.hbs +8 -0
- package/templates/channels/slack/install.template.md.hbs +49 -0
- package/templates/channels/slack/manifest.template.json.hbs +66 -0
- package/templates/channels/teams-bot/install.template.md.hbs +49 -0
- package/templates/channels/teams-bot/manifest.template.json.hbs +13 -0
- package/templates/channels/web-embed/install.template.md.hbs +49 -0
- package/templates/channels/web-embed/manifest.template.json.hbs +8 -0
- package/templates/python/docker-compose.hbs +56 -0
- package/templates/typescript/docker-compose.hbs +52 -0
package/lib/utils/paths.js
CHANGED
|
@@ -315,6 +315,30 @@ function getDevDirectory(appName, developerId) {
|
|
|
315
315
|
return baseDir;
|
|
316
316
|
}
|
|
317
317
|
|
|
318
|
+
/**
|
|
319
|
+
* Target directory for `aifabrix create` — always under {@link process.cwd} (`builder/` or
|
|
320
|
+
* `integration/`) even when those folders do not exist yet. Platform system apps still
|
|
321
|
+
* materialize under {@link getAppsMaterializationParent}.
|
|
322
|
+
*
|
|
323
|
+
* Post-create lookup uses {@link getAppPath} / {@link getBuilderPath} / {@link getIntegrationPath}.
|
|
324
|
+
*
|
|
325
|
+
* @param {string} appName - Application name
|
|
326
|
+
* @param {string} [appType] - Application type (`external` or other)
|
|
327
|
+
* @returns {string} Absolute path to the new application directory
|
|
328
|
+
*/
|
|
329
|
+
function getAppCreatePath(appName, appType) {
|
|
330
|
+
if (!appName || typeof appName !== 'string') {
|
|
331
|
+
throw new Error('App name is required and must be a string');
|
|
332
|
+
}
|
|
333
|
+
if (appType === 'external') {
|
|
334
|
+
return path.join(path.resolve(process.cwd()), 'integration', appName);
|
|
335
|
+
}
|
|
336
|
+
if (isSystemBuilderAppName(appName)) {
|
|
337
|
+
return path.join(getAppsMaterializationParent(), 'builder', appName);
|
|
338
|
+
}
|
|
339
|
+
return path.join(path.resolve(process.cwd()), 'builder', appName);
|
|
340
|
+
}
|
|
341
|
+
|
|
318
342
|
/**
|
|
319
343
|
* Gets the application path (builder or integration folder).
|
|
320
344
|
* Matches {@link getBuilderPath} / {@link getIntegrationPath} (cwd `integration/` / `builder/`, then
|
|
@@ -881,6 +905,7 @@ module.exports = {
|
|
|
881
905
|
getApplicationsBaseDir,
|
|
882
906
|
getDevDirectory,
|
|
883
907
|
getAppPath,
|
|
908
|
+
getAppCreatePath,
|
|
884
909
|
getAppsMaterializationParent,
|
|
885
910
|
getCwdIntegrationRoot,
|
|
886
911
|
getCwdBuilderRoot,
|
|
@@ -18,6 +18,7 @@ const { loadConfigFile } = require('./config-format');
|
|
|
18
18
|
const { computePublicUrlBaseString } = require('./url-declarative-public-base');
|
|
19
19
|
const { joinUrlPath, normalizeFrontDoorPatternForHealth } = require('./health-check-url');
|
|
20
20
|
const { getDevPorts } = require('./dev-config');
|
|
21
|
+
const { flagsForSetupPlatformMode } = require('../core/setup-platform-mode');
|
|
21
22
|
|
|
22
23
|
const MISO_CONTROLLER_APP = 'miso-controller';
|
|
23
24
|
|
|
@@ -74,7 +75,7 @@ function loadAppVariables(appName) {
|
|
|
74
75
|
|
|
75
76
|
function computeLocalFullPlatformFrontDoorUrl(params) {
|
|
76
77
|
const { setupPlatformMode, remoteServer, developerIdNum, frontDoorRouting } = params;
|
|
77
|
-
if (setupPlatformMode
|
|
78
|
+
if (!flagsForSetupPlatformMode(setupPlatformMode).traefik) {
|
|
78
79
|
return null;
|
|
79
80
|
}
|
|
80
81
|
if (remoteServer && String(remoteServer).trim()) {
|
|
@@ -102,12 +103,12 @@ async function computeAppBaseUrl(appName) {
|
|
|
102
103
|
const userCfg = await config.getConfig();
|
|
103
104
|
const remoteServer = await config.getRemoteServer();
|
|
104
105
|
const infraTlsEnabled = Boolean(userCfg && userCfg.tlsEnabled);
|
|
106
|
+
const setupPlatformMode = await config.getSetupPlatformMode();
|
|
105
107
|
|
|
106
108
|
const fd = variables.frontDoorRouting || null;
|
|
107
109
|
const frontDoorEnabled = Boolean(fd && fd.enabled === true);
|
|
108
110
|
const traefikOn = Boolean(userCfg && userCfg.traefik);
|
|
109
111
|
const pathActive = Boolean(traefikOn && frontDoorEnabled);
|
|
110
|
-
const setupPlatformMode = await config.getSetupPlatformMode();
|
|
111
112
|
|
|
112
113
|
const publicBase = computePublicUrlBaseString({
|
|
113
114
|
traefik: traefikOn,
|
|
@@ -119,7 +120,8 @@ async function computeAppBaseUrl(appName) {
|
|
|
119
120
|
profile: 'docker',
|
|
120
121
|
listenPort: basePort,
|
|
121
122
|
developerIdNum,
|
|
122
|
-
infraTlsEnabled
|
|
123
|
+
infraTlsEnabled,
|
|
124
|
+
setupPlatformMode
|
|
123
125
|
});
|
|
124
126
|
|
|
125
127
|
if (pathActive) {
|
|
@@ -147,10 +149,62 @@ async function resolvePlatformControllerUrl() {
|
|
|
147
149
|
return computeAppBaseUrl(MISO_CONTROLLER_APP);
|
|
148
150
|
}
|
|
149
151
|
|
|
152
|
+
/**
|
|
153
|
+
* Direct localhost controller URL (published Docker port + optional front-door mount).
|
|
154
|
+
* Used when the CLI runs on the builder host but the public Traefik URL is unreachable.
|
|
155
|
+
*
|
|
156
|
+
* @async
|
|
157
|
+
* @param {string} [appName]
|
|
158
|
+
* @returns {Promise<string>}
|
|
159
|
+
*/
|
|
160
|
+
async function resolveLocalhostPlatformControllerUrl(appName = MISO_CONTROLLER_APP) {
|
|
161
|
+
const variables = loadAppVariables(appName);
|
|
162
|
+
const developerIdRaw = await config.getDeveloperId();
|
|
163
|
+
const developerIdNum =
|
|
164
|
+
typeof developerIdRaw === 'string' ? parseInt(developerIdRaw, 10) : developerIdRaw;
|
|
165
|
+
const basePort = Number(variables.port || 3000);
|
|
166
|
+
const hostPort = basePort + (Number.isFinite(developerIdNum) ? developerIdNum : 0) * 100;
|
|
167
|
+
const fd = variables.frontDoorRouting;
|
|
168
|
+
if (fd && fd.enabled === true && fd.pattern) {
|
|
169
|
+
const mount = normalizeFrontDoorPatternForHealth(fd.pattern);
|
|
170
|
+
if (mount && mount !== '/') {
|
|
171
|
+
return joinUrlPath(`http://localhost:${hostPort}`, mount).replace(/\/+$/, '');
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return `http://localhost:${hostPort}`;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Controller URL for CLI HTTP calls on a remote dev host (builder-server).
|
|
179
|
+
* Keeps the public Traefik URL when reachable; otherwise falls back to localhost.
|
|
180
|
+
*
|
|
181
|
+
* @async
|
|
182
|
+
* @returns {Promise<string>}
|
|
183
|
+
*/
|
|
184
|
+
async function resolveCliPlatformControllerUrl() {
|
|
185
|
+
const publicUrl = await resolvePlatformControllerUrl();
|
|
186
|
+
const remoteServer = await config.getRemoteServer();
|
|
187
|
+
if (!remoteServer || !String(remoteServer).trim()) {
|
|
188
|
+
return publicUrl;
|
|
189
|
+
}
|
|
190
|
+
try {
|
|
191
|
+
const { checkHealthEndpoint } = require('./health-check-request');
|
|
192
|
+
const healthUrl = `${String(publicUrl).replace(/\/+$/, '')}/health`;
|
|
193
|
+
if (await checkHealthEndpoint(healthUrl)) {
|
|
194
|
+
return publicUrl;
|
|
195
|
+
}
|
|
196
|
+
} catch {
|
|
197
|
+
// fall through to localhost
|
|
198
|
+
}
|
|
199
|
+
return resolveLocalhostPlatformControllerUrl(MISO_CONTROLLER_APP);
|
|
200
|
+
}
|
|
201
|
+
|
|
150
202
|
module.exports = {
|
|
151
203
|
MISO_CONTROLLER_APP,
|
|
152
204
|
loadAppVariables,
|
|
153
205
|
loadBundledApplicationVariables,
|
|
154
206
|
computeAppBaseUrl,
|
|
155
|
-
resolvePlatformControllerUrl
|
|
207
|
+
resolvePlatformControllerUrl,
|
|
208
|
+
resolveLocalhostPlatformControllerUrl,
|
|
209
|
+
resolveCliPlatformControllerUrl
|
|
156
210
|
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform installation kv:// refs from infra.parameter.yaml (flat keys, no path segments).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Distinguish platform catalog secrets from per-integration path-style kv refs
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const { getInfraParameterCatalog } = require('../parameters/infra-parameter-catalog');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Returns the single-segment path after kv:// when the ref has no slashes.
|
|
15
|
+
*
|
|
16
|
+
* @param {string} val - kv:// reference
|
|
17
|
+
* @returns {string|null}
|
|
18
|
+
*/
|
|
19
|
+
function kvRefSegment(val) {
|
|
20
|
+
if (typeof val !== 'string' || !val.trim().toLowerCase().startsWith('kv://')) return null;
|
|
21
|
+
const after = val.trim().slice(5);
|
|
22
|
+
if (!after || after.includes('/')) return null;
|
|
23
|
+
return after;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* True when val is kv://<key> and key exists in shipped infra.parameter.yaml.
|
|
28
|
+
*
|
|
29
|
+
* @param {string} val - kv:// reference
|
|
30
|
+
* @returns {boolean}
|
|
31
|
+
*/
|
|
32
|
+
function isPlatformCatalogKvRef(val) {
|
|
33
|
+
const segment = kvRefSegment(val);
|
|
34
|
+
if (!segment) return false;
|
|
35
|
+
try {
|
|
36
|
+
const catalog = getInfraParameterCatalog();
|
|
37
|
+
return Boolean(catalog.findEntryForKey(segment));
|
|
38
|
+
} catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* True when repair should normalize a kv value to path-style kv://systemKey/variable.
|
|
45
|
+
* Platform catalog refs (e.g. kv://secrets-openaiApiKeyVault) are never legacy.
|
|
46
|
+
*
|
|
47
|
+
* @param {string} val - Value from authentication.security or configuration
|
|
48
|
+
* @returns {boolean}
|
|
49
|
+
*/
|
|
50
|
+
function isLegacyKvValue(val) {
|
|
51
|
+
if (typeof val !== 'string' || !val.trim().toLowerCase().startsWith('kv://')) return false;
|
|
52
|
+
if (isPlatformCatalogKvRef(val)) return false;
|
|
53
|
+
const after = val.trim().slice(5);
|
|
54
|
+
return after.includes('KeyVault') || !after.includes('/');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = { kvRefSegment, isPlatformCatalogKvRef, isLegacyKvValue };
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync runtime configuration_values from dataplane on download (values → env.template; manifest → placeholders).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Download-time runtime configuration sync
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const fsPromises = require('fs').promises;
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const yaml = require('js-yaml');
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
getExternalSystemConfigurationResolved,
|
|
16
|
+
getExternalDataSourceConfigurationResolved
|
|
17
|
+
} = require('../api/runtime-configuration.api');
|
|
18
|
+
const {
|
|
19
|
+
retemplateAllVariableDefinitions,
|
|
20
|
+
retemplateDatasourceConfigurationOverrides
|
|
21
|
+
} = require('./configuration-env-resolver');
|
|
22
|
+
const { mergeEnvTemplateWithExisting } = require('../generator/split');
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param {Object} response - Dataplane API client response
|
|
26
|
+
* @returns {Record<string, string>}
|
|
27
|
+
*/
|
|
28
|
+
function extractResolvedVariableValues(response) {
|
|
29
|
+
if (!response || !response.success) {
|
|
30
|
+
return {};
|
|
31
|
+
}
|
|
32
|
+
const body = response.data && typeof response.data === 'object' ? response.data : {};
|
|
33
|
+
const variables = body.variables && typeof body.variables === 'object' ? body.variables : {};
|
|
34
|
+
const out = {};
|
|
35
|
+
for (const [name, entry] of Object.entries(variables)) {
|
|
36
|
+
if (!entry || typeof entry.value !== 'string') {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const value = entry.value.trim();
|
|
40
|
+
if (!value || value.startsWith('kv://')) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
out[name] = value;
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {string} dataplaneUrl
|
|
50
|
+
* @param {Object} authConfig
|
|
51
|
+
* @param {string} systemKey
|
|
52
|
+
* @param {Array<{ key?: string }>} dataSources
|
|
53
|
+
* @returns {Promise<Record<string, string>>}
|
|
54
|
+
*/
|
|
55
|
+
async function collectResolvedConfigurationEnvUpdates(
|
|
56
|
+
dataplaneUrl,
|
|
57
|
+
authConfig,
|
|
58
|
+
systemKey,
|
|
59
|
+
dataSources
|
|
60
|
+
) {
|
|
61
|
+
const updates = {};
|
|
62
|
+
const systemResponse = await getExternalSystemConfigurationResolved(
|
|
63
|
+
dataplaneUrl,
|
|
64
|
+
systemKey,
|
|
65
|
+
authConfig
|
|
66
|
+
);
|
|
67
|
+
Object.assign(updates, extractResolvedVariableValues(systemResponse));
|
|
68
|
+
|
|
69
|
+
for (const ds of dataSources || []) {
|
|
70
|
+
if (!ds?.key) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const dsResponse = await getExternalDataSourceConfigurationResolved(
|
|
74
|
+
dataplaneUrl,
|
|
75
|
+
ds.key,
|
|
76
|
+
authConfig
|
|
77
|
+
);
|
|
78
|
+
Object.assign(updates, extractResolvedVariableValues(dsResponse));
|
|
79
|
+
}
|
|
80
|
+
return updates;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @param {string} integrationPath
|
|
85
|
+
* @param {Record<string, string>} updates
|
|
86
|
+
* @returns {Promise<boolean>}
|
|
87
|
+
*/
|
|
88
|
+
async function mergeConfigurationUpdatesIntoEnvTemplate(integrationPath, updates) {
|
|
89
|
+
const keys = Object.keys(updates || {});
|
|
90
|
+
if (!keys.length) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
const envTemplatePath = path.join(integrationPath, 'env.template');
|
|
94
|
+
const expectedByKey = new Map();
|
|
95
|
+
for (const key of keys) {
|
|
96
|
+
expectedByKey.set(key, `${key}=${updates[key]}`);
|
|
97
|
+
}
|
|
98
|
+
const existingContent = fs.existsSync(envTemplatePath)
|
|
99
|
+
? await fsPromises.readFile(envTemplatePath, 'utf8')
|
|
100
|
+
: '';
|
|
101
|
+
const merged = mergeEnvTemplateWithExisting(existingContent, expectedByKey);
|
|
102
|
+
await fsPromises.writeFile(envTemplatePath, merged, { mode: 0o644, encoding: 'utf8' });
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @param {string} filePath
|
|
108
|
+
* @returns {Promise<boolean>}
|
|
109
|
+
*/
|
|
110
|
+
async function retemplateConfigurationFile(filePath, { system = false } = {}) {
|
|
111
|
+
if (!filePath || !fs.existsSync(filePath)) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
const raw = await fsPromises.readFile(filePath, 'utf8');
|
|
115
|
+
const isJson = filePath.endsWith('.json');
|
|
116
|
+
const parsed = isJson ? JSON.parse(raw) : yaml.load(raw);
|
|
117
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
if (system) {
|
|
121
|
+
retemplateAllVariableDefinitions(parsed.configuration);
|
|
122
|
+
} else {
|
|
123
|
+
retemplateDatasourceConfigurationOverrides(parsed.configuration);
|
|
124
|
+
}
|
|
125
|
+
const next = isJson ? `${JSON.stringify(parsed, null, 2)}\n` : yaml.dump(parsed, { indent: 2, lineWidth: -1 });
|
|
126
|
+
await fsPromises.writeFile(filePath, next, { mode: 0o644, encoding: 'utf8' });
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @param {string} integrationPath
|
|
132
|
+
* @param {string} systemKey
|
|
133
|
+
* @returns {Promise<void>}
|
|
134
|
+
*/
|
|
135
|
+
async function retemplateDownloadedConfigurationFiles(integrationPath, systemKey) {
|
|
136
|
+
for (const ext of ['yaml', 'json']) {
|
|
137
|
+
const systemPath = path.join(integrationPath, `${systemKey}-system.${ext}`);
|
|
138
|
+
if (fs.existsSync(systemPath)) {
|
|
139
|
+
await retemplateConfigurationFile(systemPath, { system: true });
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const entries = await fsPromises.readdir(integrationPath);
|
|
143
|
+
for (const name of entries) {
|
|
144
|
+
if (!name.includes('-datasource-') || (!name.endsWith('.yaml') && !name.endsWith('.json'))) {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
await retemplateConfigurationFile(path.join(integrationPath, name), { system: false });
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Pulls configuration_values from dataplane, merges env.template, and re-templates manifests.
|
|
153
|
+
*
|
|
154
|
+
* @param {Object} params
|
|
155
|
+
* @param {string} params.dataplaneUrl
|
|
156
|
+
* @param {Object} params.authConfig
|
|
157
|
+
* @param {string} params.systemKey
|
|
158
|
+
* @param {string} params.integrationPath
|
|
159
|
+
* @param {Array<{ key?: string }>} params.dataSources
|
|
160
|
+
* @returns {Promise<{ merged: number, names: string[] }>}
|
|
161
|
+
*/
|
|
162
|
+
async function syncRuntimeConfigurationFromDataplane({
|
|
163
|
+
dataplaneUrl,
|
|
164
|
+
authConfig,
|
|
165
|
+
systemKey,
|
|
166
|
+
integrationPath,
|
|
167
|
+
dataSources
|
|
168
|
+
}) {
|
|
169
|
+
const updates = await collectResolvedConfigurationEnvUpdates(
|
|
170
|
+
dataplaneUrl,
|
|
171
|
+
authConfig,
|
|
172
|
+
systemKey,
|
|
173
|
+
dataSources
|
|
174
|
+
);
|
|
175
|
+
const names = Object.keys(updates);
|
|
176
|
+
if (names.length) {
|
|
177
|
+
await mergeConfigurationUpdatesIntoEnvTemplate(integrationPath, updates);
|
|
178
|
+
}
|
|
179
|
+
await retemplateDownloadedConfigurationFiles(integrationPath, systemKey);
|
|
180
|
+
return { merged: names.length, names };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
module.exports = {
|
|
184
|
+
extractResolvedVariableValues,
|
|
185
|
+
collectResolvedConfigurationEnvUpdates,
|
|
186
|
+
mergeConfigurationUpdatesIntoEnvTemplate,
|
|
187
|
+
retemplateDownloadedConfigurationFiles,
|
|
188
|
+
syncRuntimeConfigurationFromDataplane
|
|
189
|
+
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Push runtime configuration_values to dataplane after publish (never logs values).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Post-publish configuration PUT helpers
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const {
|
|
10
|
+
putExternalSystemConfiguration,
|
|
11
|
+
putExternalDataSourceConfiguration
|
|
12
|
+
} = require('../api/runtime-configuration.api');
|
|
13
|
+
const { buildResolvedEnvMapForIntegration } = require('./configuration-env-resolver');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param {Array<{ name?: string, location?: string }>|undefined} systemConfiguration
|
|
17
|
+
* @param {Object.<string, string>} envMap
|
|
18
|
+
* @returns {Array<{ name: string, value: string }>}
|
|
19
|
+
*/
|
|
20
|
+
function collectSystemVariablePutRequests(systemConfiguration, envMap) {
|
|
21
|
+
if (!Array.isArray(systemConfiguration)) {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
const requests = [];
|
|
25
|
+
for (const item of systemConfiguration) {
|
|
26
|
+
if (!item || !item.name) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const location = String(item.location || 'variable').toLowerCase();
|
|
30
|
+
if (location !== 'variable') {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const name = String(item.name).trim();
|
|
34
|
+
const raw = envMap[name];
|
|
35
|
+
if (raw === undefined || raw === null) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const value = String(raw).trim();
|
|
39
|
+
if (!value || value.startsWith('kv://') || value.includes('{{')) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
requests.push({ name, value });
|
|
43
|
+
}
|
|
44
|
+
return requests;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Datasource overrides are name+value only; values may already be resolved in upload payload.
|
|
49
|
+
*
|
|
50
|
+
* @param {Array<{ name?: string, value?: string }>|undefined} datasourceConfiguration
|
|
51
|
+
* @returns {Array<{ name: string, value: string }>}
|
|
52
|
+
*/
|
|
53
|
+
function collectDatasourceOverridePutRequests(datasourceConfiguration) {
|
|
54
|
+
if (!Array.isArray(datasourceConfiguration)) {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
const requests = [];
|
|
58
|
+
for (const item of datasourceConfiguration) {
|
|
59
|
+
if (!item || !item.name) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const name = String(item.name).trim();
|
|
63
|
+
const raw = item.value;
|
|
64
|
+
if (typeof raw !== 'string') {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const value = raw.trim();
|
|
68
|
+
if (!value || value.startsWith('kv://') || value.includes('{{')) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
requests.push({ name, value });
|
|
72
|
+
}
|
|
73
|
+
return requests;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Upserts system-scoped and datasource-scoped configuration values after publish.
|
|
78
|
+
*
|
|
79
|
+
* @param {string} dataplaneUrl
|
|
80
|
+
* @param {Object} authConfig
|
|
81
|
+
* @param {string} systemKey
|
|
82
|
+
* @param {{ application?: { configuration?: unknown[] }, dataSources?: Array<{ key?: string, configuration?: unknown[] }> }} payload
|
|
83
|
+
* @returns {Promise<{ pushed: number, names: string[] }>}
|
|
84
|
+
*/
|
|
85
|
+
async function pushRuntimeConfigurationValues(dataplaneUrl, authConfig, systemKey, payload) {
|
|
86
|
+
const { envMap } = await buildResolvedEnvMapForIntegration(systemKey);
|
|
87
|
+
const names = [];
|
|
88
|
+
let pushed = 0;
|
|
89
|
+
|
|
90
|
+
const systemRequests = collectSystemVariablePutRequests(
|
|
91
|
+
payload?.application?.configuration,
|
|
92
|
+
envMap
|
|
93
|
+
);
|
|
94
|
+
for (const body of systemRequests) {
|
|
95
|
+
await putExternalSystemConfiguration(dataplaneUrl, systemKey, authConfig, body);
|
|
96
|
+
names.push(body.name);
|
|
97
|
+
pushed += 1;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
for (const ds of payload?.dataSources || []) {
|
|
101
|
+
if (!ds?.key) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const dsRequests = collectDatasourceOverridePutRequests(ds.configuration);
|
|
105
|
+
for (const body of dsRequests) {
|
|
106
|
+
await putExternalDataSourceConfiguration(dataplaneUrl, ds.key, authConfig, body);
|
|
107
|
+
names.push(`${ds.key}:${body.name}`);
|
|
108
|
+
pushed += 1;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return { pushed, names };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
module.exports = {
|
|
116
|
+
collectSystemVariablePutRequests,
|
|
117
|
+
collectDatasourceOverridePutRequests,
|
|
118
|
+
pushRuntimeConfigurationValues
|
|
119
|
+
};
|
|
@@ -28,6 +28,23 @@ function resetValidators() {
|
|
|
28
28
|
externalDataSourceValidator = null;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Register AJV schemas required to compile or validate external-datasource manifests.
|
|
33
|
+
* Includes type schemas and external-system (for $defs/subscriptionFilter cross-ref).
|
|
34
|
+
*
|
|
35
|
+
* @function registerExternalDatasourceAjvDependencies
|
|
36
|
+
* @param {import('ajv').default} ajv - AJV instance
|
|
37
|
+
*/
|
|
38
|
+
function registerExternalDatasourceAjvDependencies(ajv) {
|
|
39
|
+
ajv.addSchema(require('../schema/type/document-storage.json'));
|
|
40
|
+
ajv.addSchema(require('../schema/type/message-service.json'));
|
|
41
|
+
ajv.addSchema(require('../schema/type/vector-store.json'));
|
|
42
|
+
const externalSystemSchema = require('../schema/external-system.schema.json');
|
|
43
|
+
if (!ajv.getSchema(externalSystemSchema.$id)) {
|
|
44
|
+
ajv.addSchema(externalSystemSchema, externalSystemSchema.$id);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
31
48
|
/**
|
|
32
49
|
* Loads and compiles external-system schema
|
|
33
50
|
* Caches the compiled validator for performance
|
|
@@ -115,10 +132,7 @@ function loadExternalDataSourceSchema() {
|
|
|
115
132
|
|
|
116
133
|
const ajv = new Ajv({ allErrors: true, strict: false, strictSchema: false });
|
|
117
134
|
addFormats(ajv);
|
|
118
|
-
|
|
119
|
-
ajv.addSchema(require('../schema/type/document-storage.json'));
|
|
120
|
-
ajv.addSchema(require('../schema/type/message-service.json'));
|
|
121
|
-
ajv.addSchema(require('../schema/type/vector-store.json'));
|
|
135
|
+
registerExternalDatasourceAjvDependencies(ajv);
|
|
122
136
|
externalDataSourceValidator = ajv.compile(schemaToCompile);
|
|
123
137
|
|
|
124
138
|
return externalDataSourceValidator;
|
|
@@ -333,6 +347,7 @@ function detectSchemaType(filePath, content) {
|
|
|
333
347
|
module.exports = {
|
|
334
348
|
loadExternalSystemSchema,
|
|
335
349
|
loadExternalDataSourceSchema,
|
|
350
|
+
registerExternalDatasourceAjvDependencies,
|
|
336
351
|
detectSchemaType,
|
|
337
352
|
detectSchemaTypeFromParsed,
|
|
338
353
|
resetValidators
|
|
@@ -10,26 +10,11 @@
|
|
|
10
10
|
|
|
11
11
|
const { expandFrontDoorHostPlaceholders } = require('./compose-generator');
|
|
12
12
|
const { publishedHostPort, localHostPort } = require('./declarative-url-ports');
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
* @returns {boolean}
|
|
19
|
-
*/
|
|
20
|
-
function isLocalhostPublicSchemeHostname(hostname) {
|
|
21
|
-
const h = String(hostname || '').trim().toLowerCase();
|
|
22
|
-
if (!h) {
|
|
23
|
-
return false;
|
|
24
|
-
}
|
|
25
|
-
if (h === 'localhost') {
|
|
26
|
-
return true;
|
|
27
|
-
}
|
|
28
|
-
if (h === '127.0.0.1') {
|
|
29
|
-
return true;
|
|
30
|
-
}
|
|
31
|
-
return h === '::1';
|
|
32
|
-
}
|
|
13
|
+
const {
|
|
14
|
+
normalizeSetupPlatformMode,
|
|
15
|
+
SETUP_PLATFORM_MODE
|
|
16
|
+
} = require('../core/setup-platform-mode');
|
|
17
|
+
const { isLocalhostPublicSchemeHostname } = require('./localhost-public-host');
|
|
33
18
|
|
|
34
19
|
/**
|
|
35
20
|
* Expand frontDoorRouting.host placeholders for url:// (same rules as Traefik labels in compose-generator).
|
|
@@ -156,6 +141,28 @@ function templateUsesRemoteHostPlaceholder(hostTemplate) {
|
|
|
156
141
|
return /\$\{REMOTE_HOST\}/.test(String(hostTemplate || ''));
|
|
157
142
|
}
|
|
158
143
|
|
|
144
|
+
/**
|
|
145
|
+
* HTTPS for Traefik front-door public host when infra TLS is on, remote-server is https,
|
|
146
|
+
* or setup mode is single-traefik/full (builder-server nginx edge TLS).
|
|
147
|
+
*
|
|
148
|
+
* @param {Object} opts
|
|
149
|
+
* @param {boolean} [opts.infraTlsEnabled]
|
|
150
|
+
* @param {string|null|undefined} [opts.remoteServer]
|
|
151
|
+
* @param {string|null|undefined} [opts.setupPlatformMode]
|
|
152
|
+
* @returns {boolean}
|
|
153
|
+
*/
|
|
154
|
+
function traefikFrontDoorUsesHttps(opts = {}) {
|
|
155
|
+
if (opts.infraTlsEnabled) {
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
const remote = String(opts.remoteServer || '').trim();
|
|
159
|
+
if (remote && /^https:\/\//i.test(remote)) {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
const mode = normalizeSetupPlatformMode(opts.setupPlatformMode);
|
|
163
|
+
return mode === SETUP_PLATFORM_MODE.SINGLE_TRAEFIK || mode === SETUP_PLATFORM_MODE.FULL;
|
|
164
|
+
}
|
|
165
|
+
|
|
159
166
|
/**
|
|
160
167
|
* @param {Object} opts - same shape as computePublicUrlBaseString
|
|
161
168
|
* @returns {string|null}
|
|
@@ -167,7 +174,6 @@ function buildTraefikPublicBaseIfApplicable(opts) {
|
|
|
167
174
|
hostTemplate,
|
|
168
175
|
developerIdRaw,
|
|
169
176
|
remoteServer,
|
|
170
|
-
infraTlsEnabled,
|
|
171
177
|
profile,
|
|
172
178
|
developerIdNum
|
|
173
179
|
} = opts;
|
|
@@ -189,8 +195,7 @@ function buildTraefikPublicBaseIfApplicable(opts) {
|
|
|
189
195
|
if (!expanded) {
|
|
190
196
|
return null;
|
|
191
197
|
}
|
|
192
|
-
|
|
193
|
-
const useHttps = Boolean(infraTlsEnabled);
|
|
198
|
+
const useHttps = traefikFrontDoorUsesHttps(opts);
|
|
194
199
|
let scheme = useHttps ? 'https' : 'http';
|
|
195
200
|
if (isLocalhostPublicSchemeHostname(expanded)) {
|
|
196
201
|
scheme = 'http';
|
|
@@ -210,7 +215,8 @@ function buildTraefikPublicBaseIfApplicable(opts) {
|
|
|
210
215
|
* @param {'docker'|'local'} opts.profile
|
|
211
216
|
* @param {number} opts.listenPort
|
|
212
217
|
* @param {number} opts.developerIdNum
|
|
213
|
-
* @param {boolean} [opts.infraTlsEnabled] - `tlsEnabled` from ~/.aifabrix/config.yaml (`up-infra --tls`)
|
|
218
|
+
* @param {boolean} [opts.infraTlsEnabled] - `tlsEnabled` from ~/.aifabrix/config.yaml (`up-infra --tls`). Traefik front-door public bases also use **`https`** when `remote-server` is **`https://`** or `setupPlatformMode` is **`single-traefik`** / **`full`** (builder-server nginx edge TLS). Loopback hostnames stay **http** always.
|
|
219
|
+
* @param {string|null|undefined} [opts.setupPlatformMode] - From config; used with remote-server for edge TLS scheme
|
|
214
220
|
* @param {boolean} [opts.pathActive] - traefik ∧ frontDoorRouting.enabled; required for Traefik host branch (plan 124)
|
|
215
221
|
* @param {boolean|undefined} [opts.declarativePublicUrlsUseLocalhost] - **`true`**: force localhost authority (+ port rules below). **`false`**: allow `remote-server` as authority when Traefik is on, or when global `traefik` is not `false` and per-app `proxy` opts in without Traefik. **`undefined`**: default — omit `remote-server` when Traefik is off so published services use `http://localhost:<port>` (e.g. `af setup` / device login URLs). When user config has **`traefik: false`**, expansion forces localhost for public bases regardless of per-app `proxy`.
|
|
216
222
|
* @returns {string}
|
|
@@ -270,5 +276,7 @@ function computePublicUrlBaseString(opts) {
|
|
|
270
276
|
module.exports = {
|
|
271
277
|
expandFrontDoorHostTemplateForUrls,
|
|
272
278
|
computePublicUrlBaseString,
|
|
273
|
-
resolveHostPortForDeclarativePublic
|
|
279
|
+
resolveHostPortForDeclarativePublic,
|
|
280
|
+
isLocalhostPublicSchemeHostname,
|
|
281
|
+
traefikFrontDoorUsesHttps
|
|
274
282
|
};
|