@aifabrix/builder 2.31.1 → 2.32.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -9
- package/integration/hubspot/README.md +2 -2
- package/integration/hubspot/hubspot-deploy-company.json +17 -14
- package/integration/hubspot/hubspot-deploy-contact.json +19 -16
- package/integration/hubspot/hubspot-deploy-deal.json +21 -18
- package/lib/api/types/datasources.types.js +31 -5
- package/lib/api/types/wizard.types.js +142 -0
- package/lib/api/wizard.api.js +177 -0
- package/lib/{app-config.js → app/config.js} +4 -4
- package/lib/{app-deploy.js → app/deploy.js} +8 -8
- package/lib/app/display.js +90 -0
- package/lib/{app-dockerfile.js → app/dockerfile.js} +4 -4
- package/lib/{app-down.js → app/down.js} +4 -4
- package/lib/app/helpers.js +218 -0
- package/lib/app/index.js +298 -0
- package/lib/{app-list.js → app/list.js} +6 -6
- package/lib/{app-push.js → app/push.js} +4 -4
- package/lib/{app-readme.js → app/readme.js} +34 -13
- package/lib/{app-register.js → app/register.js} +9 -9
- package/lib/{app-rotate-secret.js → app/rotate-secret.js} +10 -10
- package/lib/{app-run-helpers.js → app/run-helpers.js} +10 -10
- package/lib/{app-run.js → app/run.js} +6 -6
- package/lib/{build.js → build/index.js} +59 -32
- package/lib/build/package.json +7 -0
- package/lib/cli.js +245 -179
- package/lib/commands/app.js +3 -3
- package/lib/commands/datasource.js +4 -4
- package/lib/commands/login-credentials.js +209 -0
- package/lib/commands/login-device.js +254 -0
- package/lib/commands/login.js +67 -378
- package/lib/commands/logout.js +1 -1
- package/lib/commands/secrets-set.js +1 -1
- package/lib/commands/secure.js +2 -2
- package/lib/commands/wizard.js +498 -0
- package/lib/{audit-logger.js → core/audit-logger.js} +1 -1
- package/lib/{config.js → core/config.js} +28 -26
- package/lib/{diff.js → core/diff.js} +157 -72
- package/lib/{secrets.js → core/secrets.js} +86 -49
- package/lib/{templates.js → core/templates-env.js} +14 -222
- package/lib/core/templates.js +279 -0
- package/lib/{datasource-deploy.js → datasource/deploy.js} +6 -6
- package/lib/{datasource-diff.js → datasource/diff.js} +2 -2
- package/lib/datasource/list.js +223 -0
- package/lib/{datasource-validate.js → datasource/validate.js} +2 -2
- package/lib/{deployer.js → deployment/deployer.js} +48 -18
- package/lib/{environment-deploy.js → deployment/environment.js} +163 -84
- package/lib/{push.js → deployment/push.js} +1 -1
- package/lib/external-system/deploy-helpers.js +145 -0
- package/lib/{external-system-deploy.js → external-system/deploy.js} +156 -111
- package/lib/external-system/download-helpers.js +114 -0
- package/lib/{external-system-download.js → external-system/download.js} +92 -135
- package/lib/{external-system-generator.js → external-system/generator.js} +15 -11
- package/lib/external-system/test-auth.js +40 -0
- package/lib/external-system/test-execution.js +84 -0
- package/lib/external-system/test-helpers.js +109 -0
- package/lib/{external-system-test.js → external-system/test.js} +174 -192
- package/lib/{generator-builders.js → generator/builders.js} +87 -10
- package/lib/{generator-external.js → generator/external.js} +115 -52
- package/lib/{github-generator.js → generator/github.js} +116 -15
- package/lib/{generator-helpers.js → generator/helpers.js} +92 -42
- package/lib/{generator.js → generator/index.js} +49 -22
- package/lib/{generator-split.js → generator/split.js} +108 -55
- package/lib/generator/wizard-prompts.js +357 -0
- package/lib/generator/wizard.js +490 -0
- package/lib/{infra.js → infrastructure/index.js} +49 -22
- package/lib/schema/external-datasource.schema.json +158 -136
- package/lib/schema/external-system.schema.json +43 -1
- package/lib/utils/api.js +9 -5
- package/lib/utils/app-register-api.js +60 -32
- package/lib/utils/app-register-auth.js +172 -47
- package/lib/utils/app-register-config.js +130 -59
- package/lib/utils/app-run-containers.js +29 -8
- package/lib/utils/build-helpers.js +1 -1
- package/lib/utils/cli-utils.js +78 -30
- package/lib/utils/compose-generator.js +145 -65
- package/lib/utils/config-paths.js +2 -0
- package/lib/utils/deployment-errors.js +1 -1
- package/lib/utils/device-code.js +99 -41
- package/lib/utils/env-config-loader.js +1 -1
- package/lib/utils/env-copy.js +21 -18
- package/lib/utils/env-endpoints.js +115 -67
- package/lib/utils/env-map.js +13 -14
- package/lib/utils/env-ports.js +45 -25
- package/lib/utils/env-template.js +84 -42
- package/lib/utils/error-formatter.js +26 -9
- package/lib/utils/error-formatters/error-parser.js +90 -4
- package/lib/utils/error-formatters/http-status-errors.js +54 -17
- package/lib/utils/error-formatters/network-errors.js +103 -26
- package/lib/utils/external-system-display.js +184 -90
- package/lib/utils/external-system-validators.js +164 -42
- package/lib/utils/file-upload.js +109 -0
- package/lib/utils/health-check.js +199 -83
- package/lib/utils/infra-containers.js +1 -1
- package/lib/utils/infra-status.js +66 -15
- package/lib/utils/local-secrets.js +45 -25
- package/lib/utils/paths.js +45 -33
- package/lib/utils/schema-loader.js +42 -25
- package/lib/utils/schema-resolver.js +123 -74
- package/lib/utils/secrets-encryption.js +62 -25
- package/lib/utils/secrets-helpers.js +126 -63
- package/lib/utils/secrets-path.js +1 -1
- package/lib/utils/secrets-url.js +1 -1
- package/lib/utils/token-manager-refresh.js +181 -0
- package/lib/utils/token-manager.js +76 -123
- package/lib/utils/variable-transformer.js +154 -77
- package/lib/utils/yaml-preserve.js +41 -47
- package/lib/{template-validator.js → validation/template.js} +54 -23
- package/lib/{validate.js → validation/validate.js} +205 -125
- package/lib/{validator.js → validation/validator.js} +58 -39
- package/package.json +31 -2
- package/templates/external-system/deploy.ps1.hbs +34 -0
- package/templates/external-system/deploy.sh.hbs +34 -0
- package/templates/external-system/external-datasource.json.hbs +31 -12
- package/lib/app.js +0 -467
- package/lib/datasource-list.js +0 -141
- /package/lib/{app-prompts.js → app/prompts.js} +0 -0
- /package/lib/{env-reader.js → core/env-reader.js} +0 -0
- /package/lib/{key-generator.js → core/key-generator.js} +0 -0
package/lib/utils/env-copy.js
CHANGED
|
@@ -13,7 +13,7 @@ const path = require('path');
|
|
|
13
13
|
const yaml = require('js-yaml');
|
|
14
14
|
const chalk = require('chalk');
|
|
15
15
|
const logger = require('./logger');
|
|
16
|
-
const config = require('../config');
|
|
16
|
+
const config = require('../core/config');
|
|
17
17
|
const devConfig = require('../utils/dev-config');
|
|
18
18
|
const { rewriteInfraEndpoints } = require('./env-endpoints');
|
|
19
19
|
const { buildEnvVarMap } = require('./env-map');
|
|
@@ -126,23 +126,26 @@ function updateLocalhostUrls(envContent, baseAppPort, appPort) {
|
|
|
126
126
|
* @param {Object} envVars - Existing env vars map
|
|
127
127
|
* @returns {Object} Updated env vars map
|
|
128
128
|
*/
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
envVars.DB_HOST = dbHostMatch[1].trim();
|
|
142
|
-
}
|
|
143
|
-
if (dbPortMatch && dbPortMatch[1] && !dbPortMatch[1].includes('${')) {
|
|
144
|
-
envVars.DB_PORT = dbPortMatch[1].trim();
|
|
129
|
+
/**
|
|
130
|
+
* Extracts a single environment variable from content
|
|
131
|
+
* @function extractSingleEnvVar
|
|
132
|
+
* @param {string} envContent - Environment file content
|
|
133
|
+
* @param {string} varName - Variable name
|
|
134
|
+
* @param {Object} envVars - Environment variables object
|
|
135
|
+
*/
|
|
136
|
+
function extractSingleEnvVar(envContent, varName, envVars) {
|
|
137
|
+
const pattern = new RegExp(`^${varName}\\s*=\\s*([^\\r\\n$]+)`, 'm');
|
|
138
|
+
const match = envContent.match(pattern);
|
|
139
|
+
if (match && match[1] && !match[1].includes('${')) {
|
|
140
|
+
envVars[varName] = match[1].trim();
|
|
145
141
|
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function extractEnvVarsFromContent(envContent, envVars) {
|
|
145
|
+
extractSingleEnvVar(envContent, 'REDIS_HOST', envVars);
|
|
146
|
+
extractSingleEnvVar(envContent, 'REDIS_PORT', envVars);
|
|
147
|
+
extractSingleEnvVar(envContent, 'DB_HOST', envVars);
|
|
148
|
+
extractSingleEnvVar(envContent, 'DB_PORT', envVars);
|
|
146
149
|
return envVars;
|
|
147
150
|
}
|
|
148
151
|
|
|
@@ -205,7 +208,7 @@ async function processEnvVariables(envPath, variablesPath, appName, secretsPath)
|
|
|
205
208
|
|
|
206
209
|
// Regenerate .env file with env=local instead of copying docker-generated file
|
|
207
210
|
if (appName) {
|
|
208
|
-
const { generateEnvContent } = require('../secrets');
|
|
211
|
+
const { generateEnvContent } = require('../core/secrets');
|
|
209
212
|
const localEnvContent = await generateEnvContent(appName, secretsPath, 'local', false);
|
|
210
213
|
fs.writeFileSync(outputPath, localEnvContent, { mode: 0o600 });
|
|
211
214
|
logger.log(chalk.green(`✓ Generated local .env at: ${variables.build.envOutputPath}`));
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
const fs = require('fs');
|
|
12
12
|
const path = require('path');
|
|
13
13
|
const yaml = require('js-yaml');
|
|
14
|
-
const config = require('../config');
|
|
14
|
+
const config = require('../core/config');
|
|
15
15
|
const devConfig = require('../utils/dev-config');
|
|
16
16
|
const { loadEnvConfig } = require('./env-config-loader');
|
|
17
17
|
|
|
@@ -75,22 +75,58 @@ function getLocalhostOverride(context) {
|
|
|
75
75
|
* @param {Object} [devPorts] - Optional devPorts object with pre-adjusted ports
|
|
76
76
|
* @returns {Promise<number>} Service port with developer-id adjustment (for local context only)
|
|
77
77
|
*/
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
78
|
+
/**
|
|
79
|
+
* Gets base port from config or fallback
|
|
80
|
+
* @function getBasePortFromConfig
|
|
81
|
+
* @param {string} portKey - Port key
|
|
82
|
+
* @param {string} serviceName - Service name
|
|
83
|
+
* @param {Object} hosts - Hosts configuration
|
|
84
|
+
* @returns {number|null} Base port or null
|
|
85
|
+
*/
|
|
86
|
+
function getBasePortFromConfig(portKey, serviceName, hosts) {
|
|
86
87
|
if (hosts[portKey] !== undefined && hosts[portKey] !== null) {
|
|
87
88
|
const portVal = typeof hosts[portKey] === 'number' ? hosts[portKey] : parseInt(hosts[portKey], 10);
|
|
88
89
|
if (!Number.isNaN(portVal)) {
|
|
89
|
-
|
|
90
|
+
return portVal;
|
|
90
91
|
}
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
// Last resort fallback to devConfig (only if not in config)
|
|
95
|
+
const basePorts = devConfig.getBasePorts();
|
|
96
|
+
return basePorts[serviceName] || null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Applies developer-id adjustment to port
|
|
101
|
+
* @async
|
|
102
|
+
* @function applyDeveloperIdAdjustment
|
|
103
|
+
* @param {number} basePort - Base port
|
|
104
|
+
* @returns {Promise<number>} Adjusted port
|
|
105
|
+
*/
|
|
106
|
+
async function applyDeveloperIdAdjustment(basePort) {
|
|
107
|
+
try {
|
|
108
|
+
const devId = await config.getDeveloperId();
|
|
109
|
+
let devIdNum = 0;
|
|
110
|
+
if (devId !== null && devId !== undefined) {
|
|
111
|
+
const parsed = parseInt(devId, 10);
|
|
112
|
+
if (!Number.isNaN(parsed)) {
|
|
113
|
+
devIdNum = parsed;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return devIdNum === 0 ? basePort : (basePort + (devIdNum * 100));
|
|
117
|
+
} catch {
|
|
118
|
+
return basePort;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function getServicePort(portKey, serviceName, hosts, context, devPorts) {
|
|
123
|
+
// If devPorts provided, use it (already has developer-id adjustment)
|
|
124
|
+
if (devPorts && typeof devPorts[serviceName] === 'number') {
|
|
125
|
+
return devPorts[serviceName];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Get base port from config
|
|
129
|
+
let basePort = getBasePortFromConfig(portKey, serviceName, hosts);
|
|
94
130
|
if (basePort === null || basePort === undefined) {
|
|
95
131
|
const basePorts = devConfig.getBasePorts();
|
|
96
132
|
basePort = basePorts[serviceName];
|
|
@@ -98,19 +134,7 @@ async function getServicePort(portKey, serviceName, hosts, context, devPorts) {
|
|
|
98
134
|
|
|
99
135
|
// Apply developer-id adjustment only for local context
|
|
100
136
|
if (context === 'local') {
|
|
101
|
-
|
|
102
|
-
const devId = await config.getDeveloperId();
|
|
103
|
-
let devIdNum = 0;
|
|
104
|
-
if (devId !== null && devId !== undefined) {
|
|
105
|
-
const parsed = parseInt(devId, 10);
|
|
106
|
-
if (!Number.isNaN(parsed)) {
|
|
107
|
-
devIdNum = parsed;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return devIdNum === 0 ? basePort : (basePort + (devIdNum * 100));
|
|
111
|
-
} catch {
|
|
112
|
-
return basePort;
|
|
113
|
-
}
|
|
137
|
+
return await applyDeveloperIdAdjustment(basePort);
|
|
114
138
|
}
|
|
115
139
|
|
|
116
140
|
// For docker context, return base port without adjustment
|
|
@@ -144,9 +168,14 @@ function getServiceHost(host, context, defaultHost, localhostOverride) {
|
|
|
144
168
|
* @param {number} dbPort - Database port
|
|
145
169
|
* @returns {string} Updated content
|
|
146
170
|
*/
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
171
|
+
/**
|
|
172
|
+
* Updates Redis-related variables
|
|
173
|
+
* @function updateRedisVariables
|
|
174
|
+
* @param {string} updated - Current content
|
|
175
|
+
* @param {string} redisHost - Redis host
|
|
176
|
+
* @returns {string} Updated content
|
|
177
|
+
*/
|
|
178
|
+
function updateRedisVariables(updated, redisHost) {
|
|
150
179
|
// Update REDIS_URL if present
|
|
151
180
|
if (/^REDIS_URL\s*=.*$/m.test(updated)) {
|
|
152
181
|
updated = updated.replace(
|
|
@@ -156,31 +185,31 @@ function updateEndpointVariables(envContent, redisHost, redisPort, dbHost, dbPor
|
|
|
156
185
|
}
|
|
157
186
|
|
|
158
187
|
// Update REDIS_HOST if present
|
|
159
|
-
// If the original has host:port pattern, use ${VAR} references
|
|
160
|
-
// Otherwise, just set the host value
|
|
161
188
|
if (/^REDIS_HOST\s*=.*$/m.test(updated)) {
|
|
162
189
|
const hostPortMatch = updated.match(/^REDIS_HOST\s*=\s*([a-zA-Z0-9_.-]+):\d+$/m);
|
|
163
190
|
const hasPortPattern = !!hostPortMatch;
|
|
164
191
|
if (hasPortPattern) {
|
|
165
|
-
// Original had host:port pattern, use ${VAR} references
|
|
166
192
|
updated = updated.replace(
|
|
167
193
|
/^REDIS_HOST\s*=\s*.*$/m,
|
|
168
194
|
'REDIS_HOST=${REDIS_HOST}:${REDIS_PORT}'
|
|
169
195
|
);
|
|
170
196
|
} else {
|
|
171
|
-
// Just host, set actual value
|
|
172
197
|
updated = updated.replace(/^REDIS_HOST\s*=\s*.*$/m, `REDIS_HOST=${redisHost}`);
|
|
173
198
|
}
|
|
174
199
|
}
|
|
175
200
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
updated = updated.replace(
|
|
179
|
-
/^REDIS_PORT\s*=\s*.*$/m,
|
|
180
|
-
`REDIS_PORT=${redisPort}`
|
|
181
|
-
);
|
|
182
|
-
}
|
|
201
|
+
return updated;
|
|
202
|
+
}
|
|
183
203
|
|
|
204
|
+
/**
|
|
205
|
+
* Updates database host/port variables
|
|
206
|
+
* @function updateDatabaseVariables
|
|
207
|
+
* @param {string} updated - Current content
|
|
208
|
+
* @param {string} dbHost - Database host
|
|
209
|
+
* @param {number} dbPort - Database port
|
|
210
|
+
* @returns {string} Updated content
|
|
211
|
+
*/
|
|
212
|
+
function updateDatabaseVariables(updated, dbHost, dbPort) {
|
|
184
213
|
// Update DB_HOST if present
|
|
185
214
|
if (/^DB_HOST\s*=.*$/m.test(updated)) {
|
|
186
215
|
updated = updated.replace(/^DB_HOST\s*=\s*.*$/m, `DB_HOST=${dbHost}`);
|
|
@@ -188,58 +217,77 @@ function updateEndpointVariables(envContent, redisHost, redisPort, dbHost, dbPor
|
|
|
188
217
|
|
|
189
218
|
// Update DB_PORT if present
|
|
190
219
|
if (/^DB_PORT\s*=.*$/m.test(updated)) {
|
|
191
|
-
updated = updated.replace(
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
220
|
+
updated = updated.replace(/^DB_PORT\s*=\s*.*$/m, `DB_PORT=${dbPort}`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Update DATABASE_PORT if present
|
|
224
|
+
if (/^DATABASE_PORT\s*=.*$/m.test(updated)) {
|
|
225
|
+
updated = updated.replace(/^DATABASE_PORT\s*=\s*.*$/m, `DATABASE_PORT=${dbPort}`);
|
|
195
226
|
}
|
|
196
227
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
228
|
+
return updated;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Updates database URL variables
|
|
233
|
+
* @function updateDatabaseUrlVariables
|
|
234
|
+
* @param {string} updated - Current content
|
|
235
|
+
* @returns {string} Updated content
|
|
236
|
+
*/
|
|
237
|
+
function updateDatabaseUrlVariables(updated) {
|
|
200
238
|
const dbUrlPattern = /^(DATABASE[^=]*_URL)\s*=\s*(postgresql?:\/\/)([^:]+):([^@]+)@([^:/]+):(\d+)(\/[^\s]*)?/gm;
|
|
201
239
|
const matches = [];
|
|
202
240
|
let dbUrlMatch;
|
|
203
|
-
// Reset regex lastIndex to ensure we start from beginning
|
|
204
241
|
dbUrlPattern.lastIndex = 0;
|
|
205
242
|
while ((dbUrlMatch = dbUrlPattern.exec(updated)) !== null) {
|
|
206
243
|
matches.push({
|
|
207
|
-
varName: dbUrlMatch[1],
|
|
208
|
-
protocol: dbUrlMatch[2],
|
|
209
|
-
user: dbUrlMatch[3],
|
|
210
|
-
password: dbUrlMatch[4],
|
|
211
|
-
path: dbUrlMatch[7] || ''
|
|
244
|
+
varName: dbUrlMatch[1],
|
|
245
|
+
protocol: dbUrlMatch[2],
|
|
246
|
+
user: dbUrlMatch[3],
|
|
247
|
+
password: dbUrlMatch[4],
|
|
248
|
+
path: dbUrlMatch[7] || ''
|
|
212
249
|
});
|
|
213
250
|
}
|
|
214
|
-
// Now apply all replacements
|
|
215
251
|
for (const match of matches) {
|
|
216
252
|
updated = updated.replace(
|
|
217
253
|
new RegExp(`^${match.varName}\\s*=\\s*.*$`, 'm'),
|
|
218
254
|
`${match.varName}=${match.protocol}${match.user}:${match.password}@\${DB_HOST}:\${DB_PORT}${match.path}`
|
|
219
255
|
);
|
|
220
256
|
}
|
|
257
|
+
return updated;
|
|
258
|
+
}
|
|
221
259
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
// KC_DB_URL_HOST is used by Keycloak to connect to the database
|
|
260
|
+
/**
|
|
261
|
+
* Updates Keycloak database variables
|
|
262
|
+
* @function updateKeycloakVariables
|
|
263
|
+
* @param {string} updated - Current content
|
|
264
|
+
* @param {string} dbHost - Database host
|
|
265
|
+
* @param {number} dbPort - Database port
|
|
266
|
+
* @returns {string} Updated content
|
|
267
|
+
*/
|
|
268
|
+
function updateKeycloakVariables(updated, dbHost, dbPort) {
|
|
232
269
|
if (/^KC_DB_URL_HOST\s*=.*$/m.test(updated)) {
|
|
233
270
|
updated = updated.replace(/^KC_DB_URL_HOST\s*=\s*.*$/m, `KC_DB_URL_HOST=${dbHost}`);
|
|
234
271
|
}
|
|
235
|
-
|
|
236
|
-
// KC_DB_URL_PORT is used by Keycloak for database port
|
|
237
272
|
if (/^KC_DB_URL_PORT\s*=.*$/m.test(updated)) {
|
|
238
|
-
updated = updated.replace(
|
|
239
|
-
/^KC_DB_URL_PORT\s*=\s*.*$/m,
|
|
240
|
-
`KC_DB_URL_PORT=${dbPort}`
|
|
241
|
-
);
|
|
273
|
+
updated = updated.replace(/^KC_DB_URL_PORT\s*=\s*.*$/m, `KC_DB_URL_PORT=${dbPort}`);
|
|
242
274
|
}
|
|
275
|
+
return updated;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function updateEndpointVariables(envContent, redisHost, redisPort, dbHost, dbPort) {
|
|
279
|
+
let updated = envContent;
|
|
280
|
+
|
|
281
|
+
updated = updateRedisVariables(updated, redisHost);
|
|
282
|
+
|
|
283
|
+
// Update REDIS_PORT if present
|
|
284
|
+
if (/^REDIS_PORT\s*=.*$/m.test(updated)) {
|
|
285
|
+
updated = updated.replace(/^REDIS_PORT\s*=\s*.*$/m, `REDIS_PORT=${redisPort}`);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
updated = updateDatabaseVariables(updated, dbHost, dbPort);
|
|
289
|
+
updated = updateDatabaseUrlVariables(updated);
|
|
290
|
+
updated = updateKeycloakVariables(updated, dbHost, dbPort);
|
|
243
291
|
|
|
244
292
|
return updated;
|
|
245
293
|
}
|
package/lib/utils/env-map.js
CHANGED
|
@@ -12,7 +12,7 @@ const fs = require('fs');
|
|
|
12
12
|
const path = require('path');
|
|
13
13
|
const yaml = require('js-yaml');
|
|
14
14
|
const { loadEnvConfig } = require('./env-config-loader');
|
|
15
|
-
const config = require('../config');
|
|
15
|
+
const config = require('../core/config');
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Load base environment variables from env-config.yaml
|
|
@@ -246,19 +246,18 @@ function calculateDockerPublicPorts(result, devIdNum) {
|
|
|
246
246
|
// Match any variable ending with _PORT (e.g., MISO_PORT, KEYCLOAK_PORT, DB_PORT)
|
|
247
247
|
if (/_PORT$/.test(key) && !/_PUBLIC_PORT$/.test(key)) {
|
|
248
248
|
const publicPortKey = key.replace(/_PORT$/, '_PUBLIC_PORT');
|
|
249
|
-
//
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
}
|
|
249
|
+
// Always recalculate public port based on base port, even if manually set
|
|
250
|
+
// This ensures developer-id offset is always applied correctly
|
|
251
|
+
let portVal;
|
|
252
|
+
if (typeof value === 'string') {
|
|
253
|
+
portVal = parseInt(value, 10);
|
|
254
|
+
} else if (typeof value === 'number') {
|
|
255
|
+
portVal = value;
|
|
256
|
+
} else {
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
if (!Number.isNaN(portVal)) {
|
|
260
|
+
result[publicPortKey] = String(portVal + (devIdNum * 100));
|
|
262
261
|
}
|
|
263
262
|
}
|
|
264
263
|
}
|
package/lib/utils/env-ports.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
const fs = require('fs');
|
|
12
12
|
const yaml = require('js-yaml');
|
|
13
|
-
const config = require('../config');
|
|
13
|
+
const config = require('../core/config');
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Update PORT in the container's .env file to use variables.port (+offset)
|
|
@@ -18,37 +18,57 @@ const config = require('../config');
|
|
|
18
18
|
* @param {string} envPath - Path to .env
|
|
19
19
|
* @param {string} variablesPath - Path to variables.yaml
|
|
20
20
|
*/
|
|
21
|
+
/**
|
|
22
|
+
* Gets developer ID from environment variable or config file
|
|
23
|
+
* @function getDeveloperIdFromEnvOrConfig
|
|
24
|
+
* @returns {number} Developer ID number
|
|
25
|
+
*/
|
|
26
|
+
function getDeveloperIdFromEnvOrConfig() {
|
|
27
|
+
const devIdRaw = process.env.AIFABRIX_DEVELOPERID;
|
|
28
|
+
if (devIdRaw && /^[0-9]+$/.test(devIdRaw)) {
|
|
29
|
+
return parseInt(devIdRaw, 10);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const cfgPath = config && config.CONFIG_FILE ? config.CONFIG_FILE : null;
|
|
34
|
+
if (cfgPath && fs.existsSync(cfgPath)) {
|
|
35
|
+
const cfgContent = fs.readFileSync(cfgPath, 'utf8');
|
|
36
|
+
const cfg = yaml.load(cfgContent) || {};
|
|
37
|
+
const raw = cfg['developer-id'];
|
|
38
|
+
if (typeof raw === 'number') {
|
|
39
|
+
return raw;
|
|
40
|
+
}
|
|
41
|
+
if (typeof raw === 'string' && /^[0-9]+$/.test(raw)) {
|
|
42
|
+
return parseInt(raw, 10);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
} catch {
|
|
46
|
+
// ignore, will use 0
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return 0;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Calculates port with developer ID adjustment
|
|
54
|
+
* @function calculatePortWithDevId
|
|
55
|
+
* @param {number} basePort - Base port
|
|
56
|
+
* @param {number} devIdNum - Developer ID number
|
|
57
|
+
* @returns {number} Adjusted port
|
|
58
|
+
*/
|
|
59
|
+
function calculatePortWithDevId(basePort, devIdNum) {
|
|
60
|
+
return devIdNum > 0 ? (basePort + devIdNum * 100) : basePort;
|
|
61
|
+
}
|
|
62
|
+
|
|
21
63
|
function updateContainerPortInEnvFile(envPath, variablesPath) {
|
|
22
64
|
if (!fs.existsSync(variablesPath)) {
|
|
23
65
|
return;
|
|
24
66
|
}
|
|
25
67
|
const variablesContent = fs.readFileSync(variablesPath, 'utf8');
|
|
26
68
|
const variables = yaml.load(variablesContent);
|
|
27
|
-
// Base port from variables
|
|
28
69
|
const basePort = variables?.port || 3000;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const devIdRaw = process.env.AIFABRIX_DEVELOPERID;
|
|
32
|
-
if (devIdRaw && /^[0-9]+$/.test(devIdRaw)) {
|
|
33
|
-
devIdNum = parseInt(devIdRaw, 10);
|
|
34
|
-
} else {
|
|
35
|
-
try {
|
|
36
|
-
const cfgPath = config && config.CONFIG_FILE ? config.CONFIG_FILE : null;
|
|
37
|
-
if (cfgPath && fs.existsSync(cfgPath)) {
|
|
38
|
-
const cfgContent = fs.readFileSync(cfgPath, 'utf8');
|
|
39
|
-
const cfg = yaml.load(cfgContent) || {};
|
|
40
|
-
const raw = cfg['developer-id'];
|
|
41
|
-
if (typeof raw === 'number') {
|
|
42
|
-
devIdNum = raw;
|
|
43
|
-
} else if (typeof raw === 'string' && /^[0-9]+$/.test(raw)) {
|
|
44
|
-
devIdNum = parseInt(raw, 10);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
} catch {
|
|
48
|
-
// ignore, will use 0
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
const port = devIdNum > 0 ? (basePort + devIdNum * 100) : basePort;
|
|
70
|
+
const devIdNum = getDeveloperIdFromEnvOrConfig();
|
|
71
|
+
const port = calculatePortWithDevId(basePort, devIdNum);
|
|
52
72
|
let envContent = fs.readFileSync(envPath, 'utf8');
|
|
53
73
|
envContent = envContent.replace(/^PORT\s*=\s*.*$/m, `PORT=${port}`);
|
|
54
74
|
fs.writeFileSync(envPath, envContent, { mode: 0o600 });
|
|
@@ -24,6 +24,85 @@ const logger = require('../utils/logger');
|
|
|
24
24
|
* Note: This parameter is accepted for compatibility but the template format http://${MISO_HOST}:${MISO_PORT} is used instead
|
|
25
25
|
* @returns {Promise<void>} Resolves when template is updated
|
|
26
26
|
*/
|
|
27
|
+
/**
|
|
28
|
+
* Checks which MISO entries exist in content
|
|
29
|
+
* @function checkMisoEntries
|
|
30
|
+
* @param {string} content - File content
|
|
31
|
+
* @returns {Object} Object with boolean flags for each entry
|
|
32
|
+
*/
|
|
33
|
+
function checkMisoEntries(content) {
|
|
34
|
+
return {
|
|
35
|
+
hasClientId: /^MISO_CLIENTID\s*=/m.test(content),
|
|
36
|
+
hasClientSecret: /^MISO_CLIENTSECRET\s*=/m.test(content),
|
|
37
|
+
hasControllerUrl: /^MISO_CONTROLLER_URL\s*=/m.test(content)
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Updates existing MISO entries
|
|
43
|
+
* @function updateExistingMisoEntries
|
|
44
|
+
* @param {string} content - File content
|
|
45
|
+
* @param {string} clientIdKey - Client ID key
|
|
46
|
+
* @param {string} clientSecretKey - Client secret key
|
|
47
|
+
* @param {Object} entries - Entry flags
|
|
48
|
+
* @returns {string} Updated content
|
|
49
|
+
*/
|
|
50
|
+
function updateExistingMisoEntries(content, clientIdKey, clientSecretKey, entries) {
|
|
51
|
+
if (entries.hasClientId) {
|
|
52
|
+
content = content.replace(/^MISO_CLIENTID\s*=.*$/m, `MISO_CLIENTID=kv://${clientIdKey}`);
|
|
53
|
+
}
|
|
54
|
+
if (entries.hasClientSecret) {
|
|
55
|
+
content = content.replace(/^MISO_CLIENTSECRET\s*=.*$/m, `MISO_CLIENTSECRET=kv://${clientSecretKey}`);
|
|
56
|
+
}
|
|
57
|
+
if (entries.hasControllerUrl) {
|
|
58
|
+
content = content.replace(/^MISO_CONTROLLER_URL\s*=.*$/m, 'MISO_CONTROLLER_URL=http://${MISO_HOST}:${MISO_PORT}');
|
|
59
|
+
}
|
|
60
|
+
return content;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Builds missing MISO entries
|
|
65
|
+
* @function buildMissingMisoEntries
|
|
66
|
+
* @param {string} clientIdKey - Client ID key
|
|
67
|
+
* @param {string} clientSecretKey - Client secret key
|
|
68
|
+
* @param {Object} entries - Entry flags
|
|
69
|
+
* @returns {string[]} Array of missing entries
|
|
70
|
+
*/
|
|
71
|
+
function buildMissingMisoEntries(clientIdKey, clientSecretKey, entries) {
|
|
72
|
+
const missingEntries = [];
|
|
73
|
+
if (!entries.hasClientId) {
|
|
74
|
+
missingEntries.push(`MISO_CLIENTID=kv://${clientIdKey}`);
|
|
75
|
+
}
|
|
76
|
+
if (!entries.hasClientSecret) {
|
|
77
|
+
missingEntries.push(`MISO_CLIENTSECRET=kv://${clientSecretKey}`);
|
|
78
|
+
}
|
|
79
|
+
if (!entries.hasControllerUrl) {
|
|
80
|
+
missingEntries.push('MISO_CONTROLLER_URL=http://${MISO_HOST}:${MISO_PORT}');
|
|
81
|
+
}
|
|
82
|
+
return missingEntries;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Inserts MISO section into content
|
|
87
|
+
* @function insertMisoSection
|
|
88
|
+
* @param {string} content - File content
|
|
89
|
+
* @param {string[]} missingEntries - Missing entries to add
|
|
90
|
+
* @returns {string} Updated content
|
|
91
|
+
*/
|
|
92
|
+
function insertMisoSection(content, missingEntries) {
|
|
93
|
+
const misoSection = `# MISO Application Client Credentials (per application)
|
|
94
|
+
${missingEntries.join('\n')}
|
|
95
|
+
`;
|
|
96
|
+
|
|
97
|
+
const lastSectionMatch = content.match(/# =+.*$/gm);
|
|
98
|
+
if (lastSectionMatch && lastSectionMatch.length > 0) {
|
|
99
|
+
const lastSectionIndex = content.lastIndexOf(lastSectionMatch[lastSectionMatch.length - 1]);
|
|
100
|
+
const insertIndex = content.indexOf('\n', lastSectionIndex) + 1;
|
|
101
|
+
return content.slice(0, insertIndex) + '\n' + misoSection + content.slice(insertIndex);
|
|
102
|
+
}
|
|
103
|
+
return content + '\n' + misoSection;
|
|
104
|
+
}
|
|
105
|
+
|
|
27
106
|
async function updateEnvTemplate(appKey, clientIdKey, clientSecretKey, _controllerUrl) {
|
|
28
107
|
const envTemplatePath = path.join(process.cwd(), 'builder', appKey, 'env.template');
|
|
29
108
|
|
|
@@ -35,50 +114,13 @@ async function updateEnvTemplate(appKey, clientIdKey, clientSecretKey, _controll
|
|
|
35
114
|
try {
|
|
36
115
|
let content = await fs.readFile(envTemplatePath, 'utf8');
|
|
37
116
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const hasClientSecret = /^MISO_CLIENTSECRET\s*=/m.test(content);
|
|
41
|
-
const hasControllerUrl = /^MISO_CONTROLLER_URL\s*=/m.test(content);
|
|
42
|
-
|
|
43
|
-
// Update existing entries
|
|
44
|
-
if (hasClientId) {
|
|
45
|
-
content = content.replace(/^MISO_CLIENTID\s*=.*$/m, `MISO_CLIENTID=kv://${clientIdKey}`);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (hasClientSecret) {
|
|
49
|
-
content = content.replace(/^MISO_CLIENTSECRET\s*=.*$/m, `MISO_CLIENTSECRET=kv://${clientSecretKey}`);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (hasControllerUrl) {
|
|
53
|
-
content = content.replace(/^MISO_CONTROLLER_URL\s*=.*$/m, 'MISO_CONTROLLER_URL=http://${MISO_HOST}:${MISO_PORT}');
|
|
54
|
-
}
|
|
117
|
+
const entries = checkMisoEntries(content);
|
|
118
|
+
content = updateExistingMisoEntries(content, clientIdKey, clientSecretKey, entries);
|
|
55
119
|
|
|
56
120
|
// Add missing entries
|
|
57
|
-
if (!hasClientId || !hasClientSecret || !hasControllerUrl) {
|
|
58
|
-
const missingEntries =
|
|
59
|
-
|
|
60
|
-
missingEntries.push(`MISO_CLIENTID=kv://${clientIdKey}`);
|
|
61
|
-
}
|
|
62
|
-
if (!hasClientSecret) {
|
|
63
|
-
missingEntries.push(`MISO_CLIENTSECRET=kv://${clientSecretKey}`);
|
|
64
|
-
}
|
|
65
|
-
if (!hasControllerUrl) {
|
|
66
|
-
missingEntries.push('MISO_CONTROLLER_URL=http://${MISO_HOST}:${MISO_PORT}');
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const misoSection = `# MISO Application Client Credentials (per application)
|
|
70
|
-
${missingEntries.join('\n')}
|
|
71
|
-
`;
|
|
72
|
-
|
|
73
|
-
// Try to find a good place to insert (after last section or at end)
|
|
74
|
-
const lastSectionMatch = content.match(/# =+.*$/gm);
|
|
75
|
-
if (lastSectionMatch && lastSectionMatch.length > 0) {
|
|
76
|
-
const lastSectionIndex = content.lastIndexOf(lastSectionMatch[lastSectionMatch.length - 1]);
|
|
77
|
-
const insertIndex = content.indexOf('\n', lastSectionIndex) + 1;
|
|
78
|
-
content = content.slice(0, insertIndex) + '\n' + misoSection + content.slice(insertIndex);
|
|
79
|
-
} else {
|
|
80
|
-
content = content + '\n' + misoSection;
|
|
81
|
-
}
|
|
121
|
+
if (!entries.hasClientId || !entries.hasClientSecret || !entries.hasControllerUrl) {
|
|
122
|
+
const missingEntries = buildMissingMisoEntries(clientIdKey, clientSecretKey, entries);
|
|
123
|
+
content = insertMisoSection(content, missingEntries);
|
|
82
124
|
}
|
|
83
125
|
|
|
84
126
|
await fs.writeFile(envTemplatePath, content, 'utf8');
|
|
@@ -17,21 +17,38 @@
|
|
|
17
17
|
* @returns {string} Formatted error message
|
|
18
18
|
*/
|
|
19
19
|
function formatSingleError(error) {
|
|
20
|
-
|
|
20
|
+
// Handle empty or missing instancePath - use 'Configuration' for root level errors
|
|
21
|
+
const instancePath = error.instancePath || '';
|
|
22
|
+
const path = instancePath ? instancePath.slice(1) : '';
|
|
21
23
|
const field = path ? `Field "${path}"` : 'Configuration';
|
|
22
24
|
|
|
25
|
+
// Check if params exists before accessing it
|
|
23
26
|
const errorMessages = {
|
|
24
|
-
required:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
required: error.params?.missingProperty
|
|
28
|
+
? `${field}: Missing required property "${error.params.missingProperty}"`
|
|
29
|
+
: `${field}: Missing required property`,
|
|
30
|
+
type: error.params?.type
|
|
31
|
+
? `${field}: Expected ${error.params.type}, got ${typeof error.data}`
|
|
32
|
+
: `${field}: Type error`,
|
|
33
|
+
minimum: error.params?.limit
|
|
34
|
+
? `${field}: Value must be at least ${error.params.limit}`
|
|
35
|
+
: `${field}: Value below minimum`,
|
|
36
|
+
maximum: error.params?.limit
|
|
37
|
+
? `${field}: Value must be at most ${error.params.limit}`
|
|
38
|
+
: `${field}: Value above maximum`,
|
|
39
|
+
minLength: error.params?.limit
|
|
40
|
+
? `${field}: Must be at least ${error.params.limit} characters`
|
|
41
|
+
: `${field}: Too short`,
|
|
42
|
+
maxLength: error.params?.limit
|
|
43
|
+
? `${field}: Must be at most ${error.params.limit} characters`
|
|
44
|
+
: `${field}: Too long`,
|
|
30
45
|
pattern: `${field}: Invalid format`,
|
|
31
|
-
enum:
|
|
46
|
+
enum: error.params?.allowedValues && error.params.allowedValues.length > 0
|
|
47
|
+
? `${field}: Must be one of: ${error.params.allowedValues.join(', ')}`
|
|
48
|
+
: `${field}: Must be one of: unknown`
|
|
32
49
|
};
|
|
33
50
|
|
|
34
|
-
return errorMessages[error.keyword] || `${field}: ${error.message}`;
|
|
51
|
+
return errorMessages[error.keyword] || `${field}: ${error.message || 'Validation error'}`;
|
|
35
52
|
}
|
|
36
53
|
|
|
37
54
|
/**
|