@aifabrix/builder 2.0.0 → 2.0.3
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 +5 -3
- package/bin/aifabrix.js +9 -3
- package/jest.config.integration.js +30 -0
- package/lib/app-config.js +157 -0
- package/lib/app-deploy.js +233 -82
- package/lib/app-dockerfile.js +112 -0
- package/lib/app-prompts.js +244 -0
- package/lib/app-push.js +172 -0
- package/lib/app-run.js +235 -144
- package/lib/app.js +208 -274
- package/lib/audit-logger.js +2 -0
- package/lib/build.js +177 -125
- package/lib/cli.js +76 -86
- package/lib/commands/app.js +414 -0
- package/lib/commands/login.js +304 -0
- package/lib/config.js +78 -0
- package/lib/deployer.js +225 -81
- package/lib/env-reader.js +45 -30
- package/lib/generator.js +308 -191
- package/lib/github-generator.js +67 -7
- package/lib/infra.js +156 -61
- package/lib/push.js +105 -10
- package/lib/schema/application-schema.json +30 -2
- package/lib/schema/env-config.yaml +9 -1
- package/lib/schema/infrastructure-schema.json +589 -0
- package/lib/secrets.js +229 -24
- package/lib/template-validator.js +205 -0
- package/lib/templates.js +305 -170
- package/lib/utils/api.js +329 -0
- package/lib/utils/cli-utils.js +97 -0
- package/lib/utils/compose-generator.js +185 -0
- package/lib/utils/docker-build.js +173 -0
- package/lib/utils/dockerfile-utils.js +131 -0
- package/lib/utils/environment-checker.js +125 -0
- package/lib/utils/error-formatter.js +61 -0
- package/lib/utils/health-check.js +187 -0
- package/lib/utils/logger.js +53 -0
- package/lib/utils/template-helpers.js +223 -0
- package/lib/utils/variable-transformer.js +271 -0
- package/lib/validator.js +27 -112
- package/package.json +14 -10
- package/templates/README.md +75 -3
- package/templates/applications/keycloak/Dockerfile +36 -0
- package/templates/applications/keycloak/env.template +32 -0
- package/templates/applications/keycloak/rbac.yaml +37 -0
- package/templates/applications/keycloak/variables.yaml +56 -0
- package/templates/applications/miso-controller/Dockerfile +125 -0
- package/templates/applications/miso-controller/env.template +129 -0
- package/templates/applications/miso-controller/rbac.yaml +214 -0
- package/templates/applications/miso-controller/variables.yaml +56 -0
- package/templates/github/release.yaml.hbs +5 -26
- package/templates/github/steps/npm.hbs +24 -0
- package/templates/infra/compose.yaml +6 -6
- package/templates/python/docker-compose.hbs +19 -12
- package/templates/python/main.py +80 -0
- package/templates/python/requirements.txt +4 -0
- package/templates/typescript/Dockerfile.hbs +2 -2
- package/templates/typescript/docker-compose.hbs +19 -12
- package/templates/typescript/index.ts +116 -0
- package/templates/typescript/package.json +26 -0
- package/templates/typescript/tsconfig.json +24 -0
package/lib/validator.js
CHANGED
|
@@ -14,6 +14,9 @@ const path = require('path');
|
|
|
14
14
|
const yaml = require('js-yaml');
|
|
15
15
|
const Ajv = require('ajv');
|
|
16
16
|
const applicationSchema = require('./schema/application-schema.json');
|
|
17
|
+
const { transformVariablesForValidation } = require('./utils/variable-transformer');
|
|
18
|
+
const { checkEnvironment } = require('./utils/environment-checker');
|
|
19
|
+
const { formatValidationErrors } = require('./utils/error-formatter');
|
|
17
20
|
|
|
18
21
|
/**
|
|
19
22
|
* Validates variables.yaml file against application schema
|
|
@@ -49,9 +52,12 @@ async function validateVariables(appName) {
|
|
|
49
52
|
throw new Error(`Invalid YAML syntax in variables.yaml: ${error.message}`);
|
|
50
53
|
}
|
|
51
54
|
|
|
55
|
+
// Transform nested structure to flat schema format
|
|
56
|
+
const transformed = transformVariablesForValidation(variables, appName);
|
|
57
|
+
|
|
52
58
|
const ajv = new Ajv({ allErrors: true, strict: false });
|
|
53
59
|
const validate = ajv.compile(applicationSchema);
|
|
54
|
-
const valid = validate(
|
|
60
|
+
const valid = validate(transformed);
|
|
55
61
|
|
|
56
62
|
return {
|
|
57
63
|
valid,
|
|
@@ -209,125 +215,33 @@ async function validateEnvTemplate(appName) {
|
|
|
209
215
|
}
|
|
210
216
|
|
|
211
217
|
/**
|
|
212
|
-
*
|
|
213
|
-
*
|
|
218
|
+
* Validates deployment JSON against application schema
|
|
219
|
+
* Ensures generated aifabrix-deploy.json matches the schema structure
|
|
214
220
|
*
|
|
215
|
-
* @
|
|
216
|
-
* @
|
|
217
|
-
* @returns {
|
|
218
|
-
* @throws {Error} If critical issues are found
|
|
221
|
+
* @function validateDeploymentJson
|
|
222
|
+
* @param {Object} deployment - Deployment JSON object to validate
|
|
223
|
+
* @returns {Object} Validation result with errors
|
|
219
224
|
*
|
|
220
225
|
* @example
|
|
221
|
-
* const result =
|
|
222
|
-
* // Returns: {
|
|
226
|
+
* const result = validateDeploymentJson(deployment);
|
|
227
|
+
* // Returns: { valid: true, errors: [] }
|
|
223
228
|
*/
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
await execAsync('docker --version');
|
|
231
|
-
await execAsync('docker-compose --version');
|
|
232
|
-
return 'ok';
|
|
233
|
-
} catch (error) {
|
|
234
|
-
return 'error';
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
async function checkPorts() {
|
|
239
|
-
const requiredPorts = [5432, 6379, 5050, 8081];
|
|
240
|
-
const netstat = require('net');
|
|
241
|
-
let portIssues = 0;
|
|
242
|
-
|
|
243
|
-
for (const port of requiredPorts) {
|
|
244
|
-
try {
|
|
245
|
-
await new Promise((resolve, reject) => {
|
|
246
|
-
const server = netstat.createServer();
|
|
247
|
-
server.listen(port, () => {
|
|
248
|
-
server.close(resolve);
|
|
249
|
-
});
|
|
250
|
-
server.on('error', reject);
|
|
251
|
-
});
|
|
252
|
-
} catch (error) {
|
|
253
|
-
portIssues++;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
return portIssues === 0 ? 'ok' : 'warning';
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
function checkSecrets() {
|
|
261
|
-
const os = require('os');
|
|
262
|
-
const secretsPath = path.join(os.homedir(), '.aifabrix', 'secrets.yaml');
|
|
263
|
-
return fs.existsSync(secretsPath) ? 'ok' : 'missing';
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
async function checkEnvironment() {
|
|
267
|
-
const result = {
|
|
268
|
-
docker: 'unknown',
|
|
269
|
-
ports: 'unknown',
|
|
270
|
-
secrets: 'unknown',
|
|
271
|
-
recommendations: []
|
|
272
|
-
};
|
|
273
|
-
|
|
274
|
-
// Check Docker
|
|
275
|
-
result.docker = await checkDocker();
|
|
276
|
-
if (result.docker === 'error') {
|
|
277
|
-
result.recommendations.push('Install Docker and Docker Compose');
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// Check ports
|
|
281
|
-
result.ports = await checkPorts();
|
|
282
|
-
if (result.ports === 'warning') {
|
|
283
|
-
result.recommendations.push('Some required ports (5432, 6379, 5050, 8081) are in use');
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// Check secrets
|
|
287
|
-
result.secrets = checkSecrets();
|
|
288
|
-
if (result.secrets === 'missing') {
|
|
289
|
-
result.recommendations.push('Create secrets file: ~/.aifabrix/secrets.yaml');
|
|
229
|
+
function validateDeploymentJson(deployment) {
|
|
230
|
+
if (!deployment || typeof deployment !== 'object') {
|
|
231
|
+
return {
|
|
232
|
+
valid: false,
|
|
233
|
+
errors: ['Deployment must be an object']
|
|
234
|
+
};
|
|
290
235
|
}
|
|
291
236
|
|
|
292
|
-
|
|
293
|
-
|
|
237
|
+
const ajv = new Ajv({ allErrors: true, strict: false });
|
|
238
|
+
const validate = ajv.compile(applicationSchema);
|
|
239
|
+
const valid = validate(deployment);
|
|
294
240
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
*
|
|
299
|
-
* @function formatValidationErrors
|
|
300
|
-
* @param {Array} errors - Raw validation errors from Ajv
|
|
301
|
-
* @returns {Array} Formatted error messages
|
|
302
|
-
*
|
|
303
|
-
* @example
|
|
304
|
-
* const messages = formatValidationErrors(ajvErrors);
|
|
305
|
-
* // Returns: ['Port must be between 1 and 65535', 'Missing required field: displayName']
|
|
306
|
-
*/
|
|
307
|
-
function formatSingleError(error) {
|
|
308
|
-
const path = error.instancePath ? error.instancePath.slice(1) : 'root';
|
|
309
|
-
const field = path ? `Field "${path}"` : 'Configuration';
|
|
310
|
-
|
|
311
|
-
const errorMessages = {
|
|
312
|
-
required: `${field}: Missing required property "${error.params.missingProperty}"`,
|
|
313
|
-
type: `${field}: Expected ${error.params.type}, got ${typeof error.data}`,
|
|
314
|
-
minimum: `${field}: Value must be at least ${error.params.limit}`,
|
|
315
|
-
maximum: `${field}: Value must be at most ${error.params.limit}`,
|
|
316
|
-
minLength: `${field}: Must be at least ${error.params.limit} characters`,
|
|
317
|
-
maxLength: `${field}: Must be at most ${error.params.limit} characters`,
|
|
318
|
-
pattern: `${field}: Invalid format`,
|
|
319
|
-
enum: `${field}: Must be one of: ${error.params.allowedValues?.join(', ') || 'unknown'}`
|
|
241
|
+
return {
|
|
242
|
+
valid,
|
|
243
|
+
errors: valid ? [] : formatValidationErrors(validate.errors)
|
|
320
244
|
};
|
|
321
|
-
|
|
322
|
-
return errorMessages[error.keyword] || `${field}: ${error.message}`;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
function formatValidationErrors(errors) {
|
|
326
|
-
if (!Array.isArray(errors)) {
|
|
327
|
-
return ['Unknown validation error'];
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
return errors.map(formatSingleError);
|
|
331
245
|
}
|
|
332
246
|
|
|
333
247
|
/**
|
|
@@ -371,6 +285,7 @@ module.exports = {
|
|
|
371
285
|
validateVariables,
|
|
372
286
|
validateRbac,
|
|
373
287
|
validateEnvTemplate,
|
|
288
|
+
validateDeploymentJson,
|
|
374
289
|
checkEnvironment,
|
|
375
290
|
formatValidationErrors,
|
|
376
291
|
validateApplication
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aifabrix/builder",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3",
|
|
4
4
|
"description": "AI Fabrix Local Fabric & Deployment SDK",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -10,15 +10,18 @@
|
|
|
10
10
|
"test": "jest --coverage",
|
|
11
11
|
"test:watch": "jest --watch",
|
|
12
12
|
"test:ci": "jest --ci --coverage --watchAll=false",
|
|
13
|
+
"test:integration": "jest --config jest.config.integration.js --runInBand",
|
|
14
|
+
"test:integration:python": "cross-env TEST_LANGUAGE=python jest --config jest.config.integration.js --runInBand",
|
|
15
|
+
"test:integration:typescript": "cross-env TEST_LANGUAGE=typescript jest --config jest.config.integration.js --runInBand",
|
|
13
16
|
"lint": "eslint . --ext .js",
|
|
14
17
|
"lint:fix": "eslint . --ext .js --fix",
|
|
15
18
|
"lint:ci": "eslint . --ext .js --format json --output-file eslint-report.json",
|
|
16
19
|
"dev": "node bin/aifabrix.js",
|
|
17
20
|
"build": "npm run lint && npm run test:ci",
|
|
21
|
+
"pack": "npm run build && npm pack",
|
|
18
22
|
"validate": "npm run build",
|
|
19
23
|
"prepublishOnly": "npm run validate",
|
|
20
|
-
"precommit": "npm run lint:fix && npm run test"
|
|
21
|
-
"posttest": "npm run lint"
|
|
24
|
+
"precommit": "npm run lint:fix && npm run test"
|
|
22
25
|
},
|
|
23
26
|
"keywords": [
|
|
24
27
|
"aifabrix",
|
|
@@ -34,19 +37,20 @@
|
|
|
34
37
|
"node": ">=18.0.0"
|
|
35
38
|
},
|
|
36
39
|
"dependencies": {
|
|
37
|
-
"commander": "^11.1.0",
|
|
38
|
-
"js-yaml": "^4.1.0",
|
|
39
40
|
"ajv": "^8.12.0",
|
|
40
|
-
"handlebars": "^4.7.8",
|
|
41
41
|
"axios": "^1.6.0",
|
|
42
42
|
"chalk": "^4.1.2",
|
|
43
|
-
"
|
|
44
|
-
"
|
|
43
|
+
"commander": "^11.1.0",
|
|
44
|
+
"handlebars": "^4.7.8",
|
|
45
|
+
"inquirer": "^8.2.5",
|
|
46
|
+
"js-yaml": "^4.1.0",
|
|
47
|
+
"ora": "^5.4.1"
|
|
45
48
|
},
|
|
46
49
|
"devDependencies": {
|
|
47
|
-
"
|
|
50
|
+
"@types/node": "^20.10.0",
|
|
51
|
+
"cross-env": "^10.1.0",
|
|
48
52
|
"eslint": "^8.55.0",
|
|
49
|
-
"
|
|
53
|
+
"jest": "^29.7.0"
|
|
50
54
|
},
|
|
51
55
|
"repository": {
|
|
52
56
|
"type": "git",
|
package/templates/README.md
CHANGED
|
@@ -1,15 +1,56 @@
|
|
|
1
1
|
# AI Fabrix Builder Templates
|
|
2
2
|
|
|
3
|
-
These are Handlebars (.hbs) template files that generate Docker files for AI Fabrix applications. They should NOT be linted as Dockerfiles since they contain template variables that will be replaced during generation.
|
|
3
|
+
These are Handlebars (.hbs) template files that generate Docker files and application configurations for AI Fabrix applications. They should NOT be linted as Dockerfiles since they contain template variables that will be replaced during generation.
|
|
4
4
|
|
|
5
|
-
## Template
|
|
5
|
+
## Template Structure
|
|
6
|
+
|
|
7
|
+
The templates directory is organized as follows:
|
|
8
|
+
|
|
9
|
+
### Application Templates (for `--template` flag)
|
|
10
|
+
|
|
11
|
+
Application templates are folder-based and located under `templates/applications/`. When you use `--template <name>`, the tool looks for `templates/applications/<name>/` and copies all files from that folder to `builder/<app>/`.
|
|
12
|
+
|
|
13
|
+
**Example:**
|
|
14
|
+
- `templates/applications/miso-controller/` - Miso Controller application template
|
|
15
|
+
- `templates/applications/keycloak/` - Keycloak application template
|
|
16
|
+
|
|
17
|
+
**Template Validation:**
|
|
18
|
+
- Template folder must exist in `templates/applications/<name>/`
|
|
19
|
+
- Template folder must contain at least one file
|
|
20
|
+
- Hidden files (starting with `.`) are skipped
|
|
21
|
+
- If a template includes a `Dockerfile`, it will be copied to `builder/<app>/Dockerfile` along with other files
|
|
22
|
+
|
|
23
|
+
### Language Templates
|
|
6
24
|
|
|
7
25
|
- `python/Dockerfile.hbs` - Python application Dockerfile template
|
|
8
26
|
- `python/docker-compose.hbs` - Python application docker-compose template
|
|
9
27
|
- `typescript/Dockerfile.hbs` - TypeScript/Node.js application Dockerfile template
|
|
10
28
|
- `typescript/docker-compose.hbs` - TypeScript/Node.js application docker-compose template
|
|
29
|
+
|
|
30
|
+
### Infrastructure Templates
|
|
31
|
+
|
|
11
32
|
- `infra/compose.yaml` - Infrastructure services docker-compose template
|
|
12
33
|
|
|
34
|
+
### GitHub Workflow Templates
|
|
35
|
+
|
|
36
|
+
- `github/ci.yaml.hbs` - Continuous Integration workflow
|
|
37
|
+
- `github/pr-checks.yaml.hbs` - Pull Request checks workflow
|
|
38
|
+
- `github/release.yaml.hbs` - Release and publish workflow
|
|
39
|
+
- `github/test.yaml.hbs` - Test workflow
|
|
40
|
+
|
|
41
|
+
### GitHub Workflow Step Templates (for `--github-steps` flag)
|
|
42
|
+
|
|
43
|
+
Extra workflow steps are located in `templates/github/steps/`. When you use `--github-steps <steps>`, the tool loads step templates from `templates/github/steps/{step}.hbs` and includes them in the generated workflows.
|
|
44
|
+
|
|
45
|
+
**Example:**
|
|
46
|
+
- `github/steps/npm.hbs` - NPM publishing step
|
|
47
|
+
- `github/steps/test.hbs` - Custom test step
|
|
48
|
+
|
|
49
|
+
**Step Templates:**
|
|
50
|
+
- Step templates are Handlebars templates that generate workflow job definitions
|
|
51
|
+
- They receive the same context as the main workflow templates
|
|
52
|
+
- Step templates are rendered and included in workflow files based on the `githubSteps` array
|
|
53
|
+
|
|
13
54
|
## Template Variables
|
|
14
55
|
|
|
15
56
|
### Application Configuration
|
|
@@ -34,7 +75,38 @@ These are Handlebars (.hbs) template files that generate Docker files for AI Fab
|
|
|
34
75
|
|
|
35
76
|
## Usage
|
|
36
77
|
|
|
37
|
-
|
|
78
|
+
### Application Templates
|
|
79
|
+
|
|
80
|
+
Use `--template <name>` when creating an application:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
aifabrix create myapp --template miso-controller --port 3000
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
This validates that `templates/applications/miso-controller/` exists and copies all files (including Dockerfile if present) from it to `builder/myapp/`.
|
|
87
|
+
|
|
88
|
+
### GitHub Workflow Steps
|
|
89
|
+
|
|
90
|
+
Use `--github-steps <steps>` when creating an application with GitHub workflows:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
aifabrix create myapp --github --github-steps npm
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
This loads step templates from:
|
|
97
|
+
- `templates/github/steps/npm.hbs`
|
|
98
|
+
|
|
99
|
+
And includes them in the generated workflow files (e.g., `release.yaml`).
|
|
100
|
+
|
|
101
|
+
**Currently available step templates:**
|
|
102
|
+
- `npm.hbs` - Adds NPM publishing job to release workflow
|
|
103
|
+
|
|
104
|
+
**Creating custom step templates:**
|
|
105
|
+
Create your own step templates in `templates/github/steps/{your-step}.hbs` and reference them in `--github-steps`.
|
|
106
|
+
|
|
107
|
+
### Language and Infrastructure Templates
|
|
108
|
+
|
|
109
|
+
These templates are processed automatically by the AI Fabrix Builder SDK based on the application schema defined in `variables.yaml`. The generated files will be valid Docker files after template processing.
|
|
38
110
|
|
|
39
111
|
## VS Code Configuration
|
|
40
112
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Keycloak Identity and Access Management with Custom Themes
|
|
2
|
+
# This Dockerfile extends Keycloak 24.0 with custom themes
|
|
3
|
+
|
|
4
|
+
FROM quay.io/keycloak/keycloak:24.0
|
|
5
|
+
|
|
6
|
+
# Set working directory
|
|
7
|
+
WORKDIR /opt/keycloak
|
|
8
|
+
|
|
9
|
+
# Copy custom themes from share directory (only if themes/ folder exists)
|
|
10
|
+
USER root
|
|
11
|
+
# Use a shell script approach: copy themes if it exists, otherwise skip
|
|
12
|
+
# First, copy everything from build context to a staging area
|
|
13
|
+
COPY . /tmp/build-context/
|
|
14
|
+
# Then conditionally copy themes if the directory exists
|
|
15
|
+
RUN if [ -d "/tmp/build-context/themes" ] && [ "$(ls -A /tmp/build-context/themes)" ]; then \
|
|
16
|
+
cp -r /tmp/build-context/themes/* /opt/keycloak/themes/ && \
|
|
17
|
+
chown -R keycloak:keycloak /opt/keycloak/themes/ && \
|
|
18
|
+
echo "Themes copied successfully"; \
|
|
19
|
+
else \
|
|
20
|
+
echo "No themes directory found, skipping theme copy"; \
|
|
21
|
+
fi && \
|
|
22
|
+
rm -rf /tmp/build-context
|
|
23
|
+
|
|
24
|
+
# Build Keycloak with health checks enabled
|
|
25
|
+
# This enables the /health, /health/live, /health/ready, and /health/started endpoints
|
|
26
|
+
RUN /opt/keycloak/bin/kc.sh build --health-enabled=true
|
|
27
|
+
|
|
28
|
+
# Switch back to keycloak user
|
|
29
|
+
USER keycloak
|
|
30
|
+
|
|
31
|
+
# Keycloak runs on port 8080 internally, exposed as 8082
|
|
32
|
+
EXPOSE 8080
|
|
33
|
+
|
|
34
|
+
# Default Keycloak command (can be overridden in docker-compose.yaml)
|
|
35
|
+
# Health checks are enabled via the build step above
|
|
36
|
+
CMD ["start-dev"]
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Environment Variables Template
|
|
2
|
+
# Use kv:// references for secrets (resolved from .aifabrix/secrets.yaml)
|
|
3
|
+
# Use ${VAR} for environment-specific values
|
|
4
|
+
|
|
5
|
+
# =============================================================================
|
|
6
|
+
# APPLICATION ENVIRONMENT
|
|
7
|
+
# =============================================================================
|
|
8
|
+
|
|
9
|
+
KEYCLOAK_ADMIN=admin
|
|
10
|
+
KEYCLOAK_ADMIN_PASSWORD=kv://keycloak-admin-passwordKeyVault
|
|
11
|
+
KC_HOSTNAME_STRICT=false
|
|
12
|
+
KC_HTTP_ENABLED=true
|
|
13
|
+
|
|
14
|
+
# =============================================================================
|
|
15
|
+
# HEALTH CHECK CONFIGURATION
|
|
16
|
+
# =============================================================================
|
|
17
|
+
# Enable health check endpoints: /health, /health/live, /health/ready, /health/started
|
|
18
|
+
|
|
19
|
+
KC_HEALTH_ENABLED=true
|
|
20
|
+
|
|
21
|
+
# =============================================================================
|
|
22
|
+
# DATABASE CONFIGURATION
|
|
23
|
+
# =============================================================================
|
|
24
|
+
# Connects to postgres service in Docker network (postgres) or localhost (local)
|
|
25
|
+
|
|
26
|
+
KC_DB=postgres
|
|
27
|
+
KC_DB_URL_HOST=${DB_HOST}
|
|
28
|
+
KC_DB_URL_PORT=5432
|
|
29
|
+
KC_DB_URL_DATABASE=keycloak
|
|
30
|
+
KC_DB_USERNAME=keycloak_user
|
|
31
|
+
KC_DB_PASSWORD=kv://databases-keycloak-0-passwordKeyVault
|
|
32
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
roles:
|
|
2
|
+
- name: "AI Fabrix Admin"
|
|
3
|
+
value: "aifabrix-admin"
|
|
4
|
+
description: "Full access to all application features and configurations"
|
|
5
|
+
|
|
6
|
+
- name: "AI Fabrix User"
|
|
7
|
+
value: "aifabrix-user"
|
|
8
|
+
description: "Basic user access to the application"
|
|
9
|
+
|
|
10
|
+
- name: "AI Fabrix Developer"
|
|
11
|
+
value: "aifabrix-developer"
|
|
12
|
+
description: "Developer access for testing and debugging"
|
|
13
|
+
|
|
14
|
+
permissions:
|
|
15
|
+
- name: "myapp:read"
|
|
16
|
+
roles:
|
|
17
|
+
- "aifabrix-user"
|
|
18
|
+
- "aifabrix-admin"
|
|
19
|
+
- "aifabrix-developer"
|
|
20
|
+
description: "Read access to application data"
|
|
21
|
+
|
|
22
|
+
- name: "myapp:write"
|
|
23
|
+
roles:
|
|
24
|
+
- "aifabrix-admin"
|
|
25
|
+
- "aifabrix-developer"
|
|
26
|
+
description: "Create and edit application data"
|
|
27
|
+
|
|
28
|
+
- name: "myapp:delete"
|
|
29
|
+
roles:
|
|
30
|
+
- "aifabrix-admin"
|
|
31
|
+
description: "Delete application data"
|
|
32
|
+
|
|
33
|
+
- name: "myapp:admin"
|
|
34
|
+
roles:
|
|
35
|
+
- "aifabrix-admin"
|
|
36
|
+
description: "Administrative access to application configuration"
|
|
37
|
+
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Application Metadata
|
|
2
|
+
app:
|
|
3
|
+
key: keycloak
|
|
4
|
+
displayName: "AI Fabrix Keycloak"
|
|
5
|
+
description: "Identity and Access Management"
|
|
6
|
+
type: webapp
|
|
7
|
+
|
|
8
|
+
# Image Configuration
|
|
9
|
+
image:
|
|
10
|
+
name: aifabrix/keycloak
|
|
11
|
+
tag: "latest"
|
|
12
|
+
registry: devflowiseacr.azurecr.io
|
|
13
|
+
registryMode: acr
|
|
14
|
+
|
|
15
|
+
# Port Configuration
|
|
16
|
+
port: 8082
|
|
17
|
+
|
|
18
|
+
# Azure Requirements
|
|
19
|
+
requires:
|
|
20
|
+
database: true
|
|
21
|
+
databases:
|
|
22
|
+
- name: keycloak
|
|
23
|
+
redis: false
|
|
24
|
+
storage: false
|
|
25
|
+
|
|
26
|
+
# Health Check
|
|
27
|
+
healthCheck:
|
|
28
|
+
path: /health
|
|
29
|
+
interval: 30
|
|
30
|
+
probePath: /health
|
|
31
|
+
probeRequestType: GET
|
|
32
|
+
probeProtocol: Https
|
|
33
|
+
probeIntervalInSeconds: 120
|
|
34
|
+
|
|
35
|
+
# Authentication
|
|
36
|
+
authentication:
|
|
37
|
+
type: keycloak
|
|
38
|
+
enableSSO: true
|
|
39
|
+
requiredRoles: ["aifabrix-user"]
|
|
40
|
+
endpoints:
|
|
41
|
+
local: "http://localhost:8082/auth/callback"
|
|
42
|
+
|
|
43
|
+
# Build Configuration
|
|
44
|
+
build:
|
|
45
|
+
context: .. # Docker build context (relative to builder/)
|
|
46
|
+
dockerfile: builder/Dockerfile # Dockerfile name (empty = use template)
|
|
47
|
+
envOutputPath: .env # Copy .env to repo root for local dev
|
|
48
|
+
localPort: 8082 # Port for local development (different from Docker port)
|
|
49
|
+
containerPort: 8080 # Container port (different from local port)
|
|
50
|
+
language: typescript # Runtime language for template selection
|
|
51
|
+
secrets: # Path to secrets file (optional)
|
|
52
|
+
|
|
53
|
+
# Docker Compose
|
|
54
|
+
compose:
|
|
55
|
+
file: docker-compose.yaml
|
|
56
|
+
service: keycloak
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# AI Fabrix Miso Controller - Optimized Dockerfile
|
|
2
|
+
FROM node:18-alpine
|
|
3
|
+
|
|
4
|
+
# Install only essential runtime dependencies
|
|
5
|
+
RUN apk add --no-cache \
|
|
6
|
+
curl \
|
|
7
|
+
wget \
|
|
8
|
+
openssl \
|
|
9
|
+
openssl-dev
|
|
10
|
+
|
|
11
|
+
# Install PNPM globally
|
|
12
|
+
RUN npm install -g pnpm
|
|
13
|
+
|
|
14
|
+
# Install tsconfig-paths globally for path resolution
|
|
15
|
+
RUN npm install -g tsconfig-paths
|
|
16
|
+
|
|
17
|
+
# Set working directory
|
|
18
|
+
WORKDIR /app
|
|
19
|
+
|
|
20
|
+
# Copy package files for dependency resolution
|
|
21
|
+
COPY package*.json ./
|
|
22
|
+
COPY pnpm-workspace.yaml ./
|
|
23
|
+
COPY packages/miso-azure/package*.json ./packages/miso-azure/
|
|
24
|
+
COPY packages/miso-controller/package*.json ./packages/miso-controller/
|
|
25
|
+
COPY packages/miso-controller/bin ./packages/miso-controller/bin
|
|
26
|
+
COPY packages/miso-ui/package*.json ./packages/miso-ui/
|
|
27
|
+
|
|
28
|
+
# Create .npmrc for hoisting to fix module resolution in Docker
|
|
29
|
+
RUN echo "shamefully-hoist=true" > .npmrc
|
|
30
|
+
|
|
31
|
+
# Install all dependencies (including dev for building)
|
|
32
|
+
RUN pnpm install
|
|
33
|
+
|
|
34
|
+
# Copy only the source code we need
|
|
35
|
+
COPY packages/miso-azure/src ./packages/miso-azure/src
|
|
36
|
+
COPY packages/miso-azure/tsconfig.json ./packages/miso-azure/
|
|
37
|
+
COPY packages/miso-controller/src ./packages/miso-controller/src
|
|
38
|
+
COPY packages/miso-controller/tsconfig.json ./packages/miso-controller/
|
|
39
|
+
COPY packages/miso-controller/tsconfig.docker.json ./packages/miso-controller/
|
|
40
|
+
COPY packages/miso-controller/openapi/openapi-complete.yaml ./packages/miso-controller/dist/openapi/
|
|
41
|
+
COPY packages/miso-controller/openapi/openapi-complete.json ./packages/miso-controller/dist/openapi/
|
|
42
|
+
COPY packages/miso-ui/src ./packages/miso-ui/src
|
|
43
|
+
COPY packages/miso-ui/tsconfig.json ./packages/miso-ui/
|
|
44
|
+
COPY packages/miso-ui/tsconfig.app.json ./packages/miso-ui/
|
|
45
|
+
COPY packages/miso-ui/tsconfig.node.json ./packages/miso-ui/
|
|
46
|
+
COPY packages/miso-ui/vite.config.ts ./packages/miso-ui/
|
|
47
|
+
COPY packages/miso-ui/index.html ./packages/miso-ui/
|
|
48
|
+
|
|
49
|
+
# Fix Rollup native module issue for Alpine Linux
|
|
50
|
+
RUN pnpm add @rollup/rollup-linux-x64-musl @types/express-serve-static-core --workspace-root
|
|
51
|
+
|
|
52
|
+
# Build packages
|
|
53
|
+
WORKDIR /app/packages/miso-azure
|
|
54
|
+
RUN pnpm run build
|
|
55
|
+
|
|
56
|
+
WORKDIR /app/packages/miso-ui
|
|
57
|
+
RUN pnpm run build
|
|
58
|
+
|
|
59
|
+
WORKDIR /app/packages/miso-controller
|
|
60
|
+
RUN pnpm run db:generate
|
|
61
|
+
RUN pnpm exec tsc -p tsconfig.docker.json || true
|
|
62
|
+
# Copy sensitive-fields.config.json to dist folder
|
|
63
|
+
RUN mkdir -p dist/src/services/logging && \
|
|
64
|
+
cp src/services/logging/sensitive-fields.config.json dist/src/services/logging/ || true
|
|
65
|
+
|
|
66
|
+
# Return to root to prune correctly (needed to keep workspace dependencies)
|
|
67
|
+
WORKDIR /app
|
|
68
|
+
|
|
69
|
+
# Remove source files and build artifacts, but preserve Prisma schema files and OpenAPI files
|
|
70
|
+
RUN mkdir -p packages/miso-controller/dist/database/prisma && \
|
|
71
|
+
cp packages/miso-controller/src/database/prisma/*.prisma packages/miso-controller/dist/database/prisma/ || true
|
|
72
|
+
RUN rm -rf packages/miso-azure/src packages/miso-controller/src packages/miso-ui/src
|
|
73
|
+
RUN rm -rf packages/miso-azure/tsconfig.json packages/miso-ui/tsconfig.json
|
|
74
|
+
RUN rm -rf packages/*/node_modules packages/*/.git packages/*/.github
|
|
75
|
+
|
|
76
|
+
# Reinstall to recreate symlinks after removing source files
|
|
77
|
+
# This ensures workspace dependencies are properly linked
|
|
78
|
+
RUN pnpm install --frozen-lockfile --filter "@aifabrix/miso-controller..." || pnpm install --frozen-lockfile
|
|
79
|
+
|
|
80
|
+
# Manually remove known dev dependencies
|
|
81
|
+
RUN rm -rf node_modules/markdownlint node_modules/markdownlint-cli node_modules/prettier
|
|
82
|
+
RUN rm -rf .pnpm/markdownlint* .pnpm/prettier*
|
|
83
|
+
|
|
84
|
+
# Create non-root user
|
|
85
|
+
RUN addgroup -g 1001 -S nodejs && \
|
|
86
|
+
adduser -S miso -u 1001
|
|
87
|
+
|
|
88
|
+
# Create necessary directories for mount points (with fallback to local storage)
|
|
89
|
+
RUN mkdir -p /mnt/data/logs /mnt/data/data /mnt/data/backup /mnt/data/config
|
|
90
|
+
RUN mkdir -p /app/data/logs /app/data/data /app/data/backup /app/data/config
|
|
91
|
+
|
|
92
|
+
# Change ownership of app directory and mount points
|
|
93
|
+
RUN chown -R miso:nodejs /app /mnt/data
|
|
94
|
+
|
|
95
|
+
# Switch to non-root user
|
|
96
|
+
USER miso
|
|
97
|
+
|
|
98
|
+
# Set working directory to controller
|
|
99
|
+
WORKDIR /app/packages/miso-controller
|
|
100
|
+
|
|
101
|
+
# Expose port
|
|
102
|
+
EXPOSE 3000
|
|
103
|
+
|
|
104
|
+
# Health check
|
|
105
|
+
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
|
|
106
|
+
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
|
|
107
|
+
|
|
108
|
+
# Set environment variable for tsconfig-paths
|
|
109
|
+
ENV TS_NODE_PROJECT=tsconfig.docker.json
|
|
110
|
+
|
|
111
|
+
# Set default data paths (can be overridden by environment variables)
|
|
112
|
+
ENV LOG_PATH=/mnt/data/logs
|
|
113
|
+
ENV DATA_PATH=/mnt/data/data
|
|
114
|
+
ENV BACKUP_PATH=/mnt/data/backup
|
|
115
|
+
ENV CONFIG_PATH=/mnt/data/config
|
|
116
|
+
|
|
117
|
+
# Create symlinks for fallback to local storage if mounts are not provided
|
|
118
|
+
RUN ln -sf /app/data/logs /mnt/data/logs || true
|
|
119
|
+
RUN ln -sf /app/data/data /mnt/data/data || true
|
|
120
|
+
RUN ln -sf /app/data/backup /mnt/data/backup || true
|
|
121
|
+
RUN ln -sf /app/data/config /mnt/data/config || true
|
|
122
|
+
|
|
123
|
+
# Start the application
|
|
124
|
+
CMD ["node", "-r", "tsconfig-paths/register", "dist/src/server.js"]
|
|
125
|
+
|