@aifabrix/builder 2.0.0 → 2.0.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 +6 -2
- 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 +334 -133
- package/lib/app.js +208 -274
- package/lib/audit-logger.js +2 -0
- package/lib/build.js +209 -98
- 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/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/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 +13 -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 +168 -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/templates.js
CHANGED
|
@@ -9,41 +9,88 @@ const yaml = require('js-yaml');
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Generate variables.yaml content for an application
|
|
12
|
+
* Matches application-schema.json structure
|
|
12
13
|
* @param {string} appName - Application name
|
|
13
14
|
* @param {Object} config - Configuration options
|
|
14
15
|
* @returns {string} YAML content
|
|
15
16
|
*/
|
|
16
17
|
function generateVariablesYaml(appName, config) {
|
|
18
|
+
const displayName = appName.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
|
|
19
|
+
|
|
20
|
+
// Parse image name to separate name and tag
|
|
21
|
+
let imageName = appName;
|
|
22
|
+
let imageTag = 'latest';
|
|
23
|
+
const imageNameWithTag = `${appName}:latest`;
|
|
24
|
+
const colonIndex = imageNameWithTag.lastIndexOf(':');
|
|
25
|
+
if (colonIndex !== -1) {
|
|
26
|
+
imageName = imageNameWithTag.substring(0, colonIndex);
|
|
27
|
+
imageTag = imageNameWithTag.substring(colonIndex + 1);
|
|
28
|
+
}
|
|
29
|
+
|
|
17
30
|
const variables = {
|
|
18
31
|
app: {
|
|
19
32
|
key: appName,
|
|
20
|
-
|
|
33
|
+
displayName: displayName,
|
|
21
34
|
description: `${appName.replace(/-/g, ' ')} application`,
|
|
22
|
-
|
|
35
|
+
type: 'webapp'
|
|
23
36
|
},
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
37
|
+
image: {
|
|
38
|
+
name: imageName,
|
|
39
|
+
tag: imageTag,
|
|
40
|
+
registry: '',
|
|
41
|
+
registryMode: 'external'
|
|
28
42
|
},
|
|
29
|
-
|
|
43
|
+
port: parseInt(config.port, 10) || 3000,
|
|
44
|
+
requires: {
|
|
30
45
|
database: config.database || false,
|
|
31
46
|
redis: config.redis || false,
|
|
32
|
-
storage: config.storage || false
|
|
33
|
-
authentication: config.authentication || false
|
|
47
|
+
storage: config.storage || false
|
|
34
48
|
},
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
49
|
+
build: {
|
|
50
|
+
language: config.language || 'typescript',
|
|
51
|
+
envOutputPath: null,
|
|
52
|
+
context: `../${appName}`,
|
|
53
|
+
dockerfile: '',
|
|
54
|
+
secrets: null
|
|
39
55
|
},
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
56
|
+
repository: {
|
|
57
|
+
enabled: false,
|
|
58
|
+
repositoryUrl: ''
|
|
59
|
+
},
|
|
60
|
+
deployment: {
|
|
61
|
+
controllerUrl: '',
|
|
62
|
+
environment: 'dev',
|
|
63
|
+
clientId: '',
|
|
64
|
+
clientSecret: ''
|
|
44
65
|
}
|
|
45
66
|
};
|
|
46
67
|
|
|
68
|
+
// Add databases array when database is enabled
|
|
69
|
+
if (config.database) {
|
|
70
|
+
// Database names must match schema pattern: ^[a-z0-9_]+$ (no hyphens)
|
|
71
|
+
const dbName = appName.replace(/-/g, '_');
|
|
72
|
+
variables.requires.databases = [
|
|
73
|
+
{ name: dbName }
|
|
74
|
+
];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Add optional healthCheck at top level
|
|
78
|
+
if (config.healthCheck !== false) {
|
|
79
|
+
variables.healthCheck = {
|
|
80
|
+
path: '/health',
|
|
81
|
+
interval: 30
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Add optional authentication at top level
|
|
86
|
+
if (config.authentication) {
|
|
87
|
+
variables.authentication = {
|
|
88
|
+
type: 'azure',
|
|
89
|
+
enableSSO: true,
|
|
90
|
+
requiredRoles: ['aifabrix-user']
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
47
94
|
return yaml.dump(variables, {
|
|
48
95
|
indent: 2,
|
|
49
96
|
lineWidth: 120,
|
|
@@ -53,185 +100,295 @@ function generateVariablesYaml(appName, config) {
|
|
|
53
100
|
}
|
|
54
101
|
|
|
55
102
|
/**
|
|
56
|
-
*
|
|
103
|
+
* Builds core application environment variables
|
|
57
104
|
* @param {Object} config - Configuration options
|
|
58
|
-
* @
|
|
59
|
-
* @returns {string} Environment template content
|
|
105
|
+
* @returns {Object} Core environment variables
|
|
60
106
|
*/
|
|
61
|
-
function
|
|
62
|
-
|
|
63
|
-
// Core application settings
|
|
107
|
+
function buildCoreEnv(config) {
|
|
108
|
+
return {
|
|
64
109
|
'NODE_ENV': 'development',
|
|
65
110
|
'PORT': config.port || 3000,
|
|
66
111
|
'APP_NAME': config.appName || 'myapp',
|
|
67
112
|
'LOG_LEVEL': 'info'
|
|
68
113
|
};
|
|
114
|
+
}
|
|
69
115
|
|
|
70
|
-
|
|
71
|
-
if (config.database) {
|
|
72
|
-
|
|
73
|
-
envVars['DB_HOST'] = 'localhost';
|
|
74
|
-
envVars['DB_PORT'] = '5432';
|
|
75
|
-
envVars['DB_NAME'] = config.appName || 'myapp';
|
|
76
|
-
envVars['DB_USER'] = 'kv://database-user';
|
|
77
|
-
envVars['DB_PASSWORD'] = 'kv://database-password';
|
|
116
|
+
function buildDatabaseEnv(config) {
|
|
117
|
+
if (!config.database) {
|
|
118
|
+
return {};
|
|
78
119
|
}
|
|
79
120
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
121
|
+
const appName = config.appName || 'myapp';
|
|
122
|
+
// Database names must use underscores (PostgreSQL doesn't allow hyphens)
|
|
123
|
+
const dbName = appName.replace(/-/g, '_');
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
'DATABASE_URL': `kv://databases-${appName}-0-urlKeyVault`,
|
|
127
|
+
'DB_HOST': '${DB_HOST}',
|
|
128
|
+
'DB_PORT': '5432',
|
|
129
|
+
'DB_NAME': dbName,
|
|
130
|
+
'DB_USER': `${dbName}_user`,
|
|
131
|
+
'DB_PASSWORD': `kv://databases-${appName}-0-passwordKeyVault`
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function buildRedisEnv(config) {
|
|
136
|
+
if (!config.redis) {
|
|
137
|
+
return {};
|
|
86
138
|
}
|
|
87
139
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
140
|
+
return {
|
|
141
|
+
'REDIS_URL': 'kv://redis-url',
|
|
142
|
+
'REDIS_HOST': '${REDIS_HOST}',
|
|
143
|
+
'REDIS_PORT': '6379',
|
|
144
|
+
'REDIS_PASSWORD': 'kv://redis-password'
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function buildStorageEnv(config) {
|
|
149
|
+
if (!config.storage) {
|
|
150
|
+
return {};
|
|
95
151
|
}
|
|
96
152
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
153
|
+
return {
|
|
154
|
+
'STORAGE_TYPE': 'local',
|
|
155
|
+
'STORAGE_PATH': '/app/storage',
|
|
156
|
+
'STORAGE_URL': 'kv://storage-url',
|
|
157
|
+
'STORAGE_KEY': 'kv://storage-key',
|
|
158
|
+
'STORAGE_SECRET': 'kv://storage-secret'
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function buildAuthEnv(config) {
|
|
163
|
+
if (!config.authentication) {
|
|
164
|
+
return {};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
'JWT_SECRET': 'kv://jwt-secret',
|
|
169
|
+
'JWT_EXPIRES_IN': '24h',
|
|
170
|
+
'AUTH_PROVIDER': 'local',
|
|
171
|
+
'SESSION_SECRET': 'kv://session-secret'
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function buildMonitoringEnv(config) {
|
|
176
|
+
if (!config.controller) {
|
|
177
|
+
return {};
|
|
103
178
|
}
|
|
104
179
|
|
|
180
|
+
return {
|
|
181
|
+
'MISO_CONTROLLER_URL': config.controllerUrl || 'https://controller.aifabrix.ai',
|
|
182
|
+
'MISO_ENVIRONMENT': 'dev',
|
|
183
|
+
'MISO_CLIENTID': 'kv://miso-clientid',
|
|
184
|
+
'MISO_CLIENTSECRET': 'kv://miso-clientsecret'
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Adds core variables section to template lines
|
|
190
|
+
* @param {Array<string>} lines - Template lines array
|
|
191
|
+
* @param {Object} envVars - Environment variables
|
|
192
|
+
*/
|
|
193
|
+
function addCoreVariables(lines, envVars) {
|
|
194
|
+
Object.entries(envVars).forEach(([key, value]) => {
|
|
195
|
+
if (key.startsWith('NODE_ENV') || key.startsWith('PORT') ||
|
|
196
|
+
key.startsWith('APP_NAME') || key.startsWith('LOG_LEVEL')) {
|
|
197
|
+
lines.push(`${key}=${value}`);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function addMonitoringSection(lines, envVars) {
|
|
203
|
+
lines.push('', '# MISO Controller Configuration', '');
|
|
204
|
+
lines.push(`MISO_CONTROLLER_URL=${envVars['MISO_CONTROLLER_URL']}`);
|
|
205
|
+
lines.push(`MISO_ENVIRONMENT=${envVars['MISO_ENVIRONMENT']}`);
|
|
206
|
+
lines.push(`MISO_CLIENTID=${envVars['MISO_CLIENTID']}`);
|
|
207
|
+
lines.push(`MISO_CLIENTSECRET=${envVars['MISO_CLIENTSECRET']}`);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function addDatabaseSection(lines, envVars) {
|
|
211
|
+
lines.push('', '# =============================================================================');
|
|
212
|
+
lines.push('# DATABASE CONFIGURATION');
|
|
213
|
+
lines.push('# =============================================================================');
|
|
214
|
+
lines.push('# Connects to external postgres from aifabrix-setup');
|
|
215
|
+
lines.push('');
|
|
216
|
+
// Add all DB_* and DATABASE_* variables together (sorted for consistency)
|
|
217
|
+
const dbVars = Object.entries(envVars)
|
|
218
|
+
.filter(([key]) => key.startsWith('DB_') || key.startsWith('DATABASE_'))
|
|
219
|
+
.sort(([a], [b]) => {
|
|
220
|
+
// Sort DB_* before DATABASE_*, then alphabetically
|
|
221
|
+
const aPrefix = a.startsWith('DB_') ? 0 : 1;
|
|
222
|
+
const bPrefix = b.startsWith('DB_') ? 0 : 1;
|
|
223
|
+
if (aPrefix !== bPrefix) return aPrefix - bPrefix;
|
|
224
|
+
return a.localeCompare(b);
|
|
225
|
+
});
|
|
226
|
+
dbVars.forEach(([key, value]) => {
|
|
227
|
+
lines.push(`${key}=${value}`);
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function addRedisSection(lines, envVars) {
|
|
232
|
+
lines.push('', '# =============================================================================');
|
|
233
|
+
lines.push('# REDIS CONFIGURATION');
|
|
234
|
+
lines.push('# =============================================================================');
|
|
235
|
+
lines.push('# Connects to external redis from aifabrix-setup');
|
|
236
|
+
lines.push('');
|
|
237
|
+
Object.entries(envVars).forEach(([key, value]) => {
|
|
238
|
+
if (key.startsWith('REDIS_')) {
|
|
239
|
+
lines.push(`${key}=${value}`);
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function addStorageSection(lines, envVars) {
|
|
245
|
+
lines.push('', '# =============================================================================');
|
|
246
|
+
lines.push('# STORAGE CONFIGURATION');
|
|
247
|
+
lines.push('# =============================================================================');
|
|
248
|
+
lines.push('');
|
|
249
|
+
Object.entries(envVars).forEach(([key, value]) => {
|
|
250
|
+
if (key.startsWith('STORAGE_')) {
|
|
251
|
+
lines.push(`${key}=${value}`);
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function addAuthenticationSection(lines, envVars) {
|
|
257
|
+
lines.push('', '# =============================================================================');
|
|
258
|
+
lines.push('# AUTHENTICATION CONFIGURATION');
|
|
259
|
+
lines.push('# =============================================================================');
|
|
260
|
+
lines.push('# Connects to external keycloak from aifabrix-setup');
|
|
261
|
+
lines.push('');
|
|
262
|
+
// Add all JWT_*, AUTH_*, and SESSION_* variables together (sorted for consistency)
|
|
263
|
+
const authVars = Object.entries(envVars)
|
|
264
|
+
.filter(([key]) => key.startsWith('JWT_') || key.startsWith('AUTH_') || key.startsWith('SESSION_'))
|
|
265
|
+
.sort(([a], [b]) => {
|
|
266
|
+
// Sort by prefix priority: JWT_ (0), AUTH_ (1), SESSION_ (2), then alphabetically
|
|
267
|
+
const getPrefixPriority = (key) => {
|
|
268
|
+
if (key.startsWith('JWT_')) return 0;
|
|
269
|
+
if (key.startsWith('AUTH_')) return 1;
|
|
270
|
+
if (key.startsWith('SESSION_')) return 2;
|
|
271
|
+
return 3;
|
|
272
|
+
};
|
|
273
|
+
const aPriority = getPrefixPriority(a);
|
|
274
|
+
const bPriority = getPrefixPriority(b);
|
|
275
|
+
if (aPriority !== bPriority) return aPriority - bPriority;
|
|
276
|
+
return a.localeCompare(b);
|
|
277
|
+
});
|
|
278
|
+
authVars.forEach(([key, value]) => {
|
|
279
|
+
lines.push(`${key}=${value}`);
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Generate env.template content with conditional variables
|
|
285
|
+
* @param {Object} config - Configuration options
|
|
286
|
+
* @param {Object} existingEnv - Existing environment variables
|
|
287
|
+
* @returns {string} Environment template content
|
|
288
|
+
*/
|
|
289
|
+
function generateEnvTemplate(config, existingEnv = {}) {
|
|
290
|
+
const envVars = {
|
|
291
|
+
...buildCoreEnv(config),
|
|
292
|
+
...buildDatabaseEnv(config),
|
|
293
|
+
...buildRedisEnv(config),
|
|
294
|
+
...buildStorageEnv(config),
|
|
295
|
+
...buildAuthEnv(config),
|
|
296
|
+
...buildMonitoringEnv(config)
|
|
297
|
+
};
|
|
298
|
+
|
|
105
299
|
// Merge with existing environment variables
|
|
106
300
|
Object.assign(envVars, existingEnv);
|
|
107
301
|
|
|
108
302
|
// Generate template content
|
|
109
303
|
const lines = [
|
|
110
|
-
'#
|
|
111
|
-
'#
|
|
112
|
-
'#
|
|
304
|
+
'# Environment Variables Template',
|
|
305
|
+
'# Use kv:// references for secrets (resolved from .aifabrix/secrets.yaml)',
|
|
306
|
+
'# Use ${VAR} for environment-specific values',
|
|
113
307
|
'',
|
|
114
|
-
'#
|
|
308
|
+
'# =============================================================================',
|
|
309
|
+
'# APPLICATION ENVIRONMENT',
|
|
310
|
+
'# =============================================================================',
|
|
115
311
|
''
|
|
116
312
|
];
|
|
117
313
|
|
|
118
314
|
// Add core variables
|
|
119
|
-
|
|
120
|
-
if (key.startsWith('NODE_ENV') || key.startsWith('PORT') ||
|
|
121
|
-
key.startsWith('APP_NAME') || key.startsWith('LOG_LEVEL')) {
|
|
122
|
-
lines.push(`${key}=${value}`);
|
|
123
|
-
}
|
|
124
|
-
});
|
|
315
|
+
addCoreVariables(lines, envVars);
|
|
125
316
|
|
|
126
317
|
// Add service-specific sections
|
|
127
318
|
if (config.database) {
|
|
128
|
-
lines
|
|
129
|
-
Object.entries(envVars).forEach(([key, value]) => {
|
|
130
|
-
if (key.startsWith('DB_') || key.startsWith('DATABASE_')) {
|
|
131
|
-
lines.push(`${key}=${value}`);
|
|
132
|
-
}
|
|
133
|
-
});
|
|
319
|
+
addDatabaseSection(lines, envVars);
|
|
134
320
|
}
|
|
135
321
|
|
|
136
322
|
if (config.redis) {
|
|
137
|
-
lines
|
|
138
|
-
Object.entries(envVars).forEach(([key, value]) => {
|
|
139
|
-
if (key.startsWith('REDIS_')) {
|
|
140
|
-
lines.push(`${key}=${value}`);
|
|
141
|
-
}
|
|
142
|
-
});
|
|
323
|
+
addRedisSection(lines, envVars);
|
|
143
324
|
}
|
|
144
325
|
|
|
145
326
|
if (config.storage) {
|
|
146
|
-
lines
|
|
147
|
-
Object.entries(envVars).forEach(([key, value]) => {
|
|
148
|
-
if (key.startsWith('STORAGE_')) {
|
|
149
|
-
lines.push(`${key}=${value}`);
|
|
150
|
-
}
|
|
151
|
-
});
|
|
327
|
+
addStorageSection(lines, envVars);
|
|
152
328
|
}
|
|
153
329
|
|
|
154
330
|
if (config.authentication) {
|
|
155
|
-
lines
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
});
|
|
331
|
+
addAuthenticationSection(lines, envVars);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (config.controller) {
|
|
335
|
+
addMonitoringSection(lines, envVars);
|
|
161
336
|
}
|
|
162
337
|
|
|
163
338
|
return lines.join('\n');
|
|
164
339
|
}
|
|
165
340
|
|
|
166
|
-
/**
|
|
167
|
-
* Generate rbac.yaml content for RBAC configuration
|
|
168
|
-
* @param {string} appName - Application name
|
|
169
|
-
* @param {Object} config - Configuration options
|
|
170
|
-
* @returns {string} RBAC YAML content
|
|
171
|
-
*/
|
|
172
341
|
function generateRbacYaml(appName, config) {
|
|
173
342
|
if (!config.authentication) {
|
|
174
343
|
return null;
|
|
175
344
|
}
|
|
176
345
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
346
|
+
// Format roles with name, value, and description
|
|
347
|
+
const roles = [
|
|
348
|
+
{
|
|
349
|
+
name: 'AI Fabrix Admin',
|
|
350
|
+
value: 'aifabrix-admin',
|
|
351
|
+
description: 'Full access to all application features and configurations'
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
name: 'AI Fabrix User',
|
|
355
|
+
value: 'aifabrix-user',
|
|
356
|
+
description: 'Basic user access to the application'
|
|
183
357
|
},
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
description: 'Full administrative access',
|
|
189
|
-
permissions: ['*']
|
|
190
|
-
},
|
|
191
|
-
{
|
|
192
|
-
name: 'user',
|
|
193
|
-
description: 'Standard user access',
|
|
194
|
-
permissions: ['read', 'write']
|
|
195
|
-
},
|
|
196
|
-
{
|
|
197
|
-
name: 'viewer',
|
|
198
|
-
description: 'Read-only access',
|
|
199
|
-
permissions: ['read']
|
|
200
|
-
}
|
|
201
|
-
],
|
|
202
|
-
policies: [
|
|
203
|
-
{
|
|
204
|
-
name: 'admin-policy',
|
|
205
|
-
role: 'admin',
|
|
206
|
-
resources: ['*'],
|
|
207
|
-
actions: ['*']
|
|
208
|
-
},
|
|
209
|
-
{
|
|
210
|
-
name: 'user-policy',
|
|
211
|
-
role: 'user',
|
|
212
|
-
resources: ['data', 'profile'],
|
|
213
|
-
actions: ['read', 'write']
|
|
214
|
-
},
|
|
215
|
-
{
|
|
216
|
-
name: 'viewer-policy',
|
|
217
|
-
role: 'viewer',
|
|
218
|
-
resources: ['data'],
|
|
219
|
-
actions: ['read']
|
|
220
|
-
}
|
|
221
|
-
],
|
|
222
|
-
bindings: [
|
|
223
|
-
{
|
|
224
|
-
name: 'admin-binding',
|
|
225
|
-
role: 'admin',
|
|
226
|
-
subjects: [
|
|
227
|
-
{
|
|
228
|
-
kind: 'User',
|
|
229
|
-
name: 'admin@example.com'
|
|
230
|
-
}
|
|
231
|
-
]
|
|
232
|
-
}
|
|
233
|
-
]
|
|
358
|
+
{
|
|
359
|
+
name: 'AI Fabrix Developer',
|
|
360
|
+
value: 'aifabrix-developer',
|
|
361
|
+
description: 'Developer access for testing and debugging'
|
|
234
362
|
}
|
|
363
|
+
];
|
|
364
|
+
|
|
365
|
+
// Format permissions with name, roles array, and description
|
|
366
|
+
const permissions = [
|
|
367
|
+
{
|
|
368
|
+
name: `${appName}:read`,
|
|
369
|
+
roles: ['aifabrix-user', 'aifabrix-admin', 'aifabrix-developer'],
|
|
370
|
+
description: 'Read access to application data'
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
name: `${appName}:write`,
|
|
374
|
+
roles: ['aifabrix-admin', 'aifabrix-developer'],
|
|
375
|
+
description: 'Create and edit application data'
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
name: `${appName}:delete`,
|
|
379
|
+
roles: ['aifabrix-admin'],
|
|
380
|
+
description: 'Delete application data'
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
name: `${appName}:admin`,
|
|
384
|
+
roles: ['aifabrix-admin'],
|
|
385
|
+
description: 'Administrative access to application configuration'
|
|
386
|
+
}
|
|
387
|
+
];
|
|
388
|
+
|
|
389
|
+
const rbac = {
|
|
390
|
+
roles: roles,
|
|
391
|
+
permissions: permissions
|
|
235
392
|
};
|
|
236
393
|
|
|
237
394
|
return yaml.dump(rbac, {
|
|
@@ -242,55 +399,33 @@ function generateRbacYaml(appName, config) {
|
|
|
242
399
|
});
|
|
243
400
|
}
|
|
244
401
|
|
|
245
|
-
/**
|
|
246
|
-
* Generate secrets.yaml content for sensitive values
|
|
247
|
-
* @param {Object} config - Configuration options
|
|
248
|
-
* @param {Object} existingSecrets - Existing secrets from .env
|
|
249
|
-
* @returns {string} Secrets YAML content
|
|
250
|
-
*/
|
|
251
402
|
function generateSecretsYaml(config, existingSecrets = {}) {
|
|
252
403
|
const secrets = {
|
|
253
404
|
apiVersion: 'v1',
|
|
254
405
|
kind: 'Secret',
|
|
255
|
-
metadata: {
|
|
256
|
-
name: 'app-secrets',
|
|
257
|
-
namespace: 'default'
|
|
258
|
-
},
|
|
406
|
+
metadata: { name: 'app-secrets', namespace: 'default' },
|
|
259
407
|
type: 'Opaque',
|
|
260
408
|
data: {}
|
|
261
409
|
};
|
|
262
|
-
|
|
263
|
-
// Add secrets based on enabled services
|
|
264
410
|
if (config.database) {
|
|
265
411
|
secrets.data['database-password'] = 'base64-encoded-password';
|
|
266
412
|
secrets.data['database-user'] = 'base64-encoded-user';
|
|
267
413
|
}
|
|
268
|
-
|
|
269
414
|
if (config.redis) {
|
|
270
415
|
secrets.data['redis-password'] = 'base64-encoded-redis-password';
|
|
271
416
|
}
|
|
272
|
-
|
|
273
417
|
if (config.storage) {
|
|
274
418
|
secrets.data['storage-key'] = 'base64-encoded-storage-key';
|
|
275
419
|
secrets.data['storage-secret'] = 'base64-encoded-storage-secret';
|
|
276
420
|
}
|
|
277
|
-
|
|
278
421
|
if (config.authentication) {
|
|
279
422
|
secrets.data['jwt-secret'] = 'base64-encoded-jwt-secret';
|
|
280
423
|
secrets.data['session-secret'] = 'base64-encoded-session-secret';
|
|
281
424
|
}
|
|
282
|
-
|
|
283
|
-
// Add existing secrets
|
|
284
425
|
Object.entries(existingSecrets).forEach(([key, value]) => {
|
|
285
426
|
secrets.data[key] = Buffer.from(value).toString('base64');
|
|
286
427
|
});
|
|
287
|
-
|
|
288
|
-
return yaml.dump(secrets, {
|
|
289
|
-
indent: 2,
|
|
290
|
-
lineWidth: 120,
|
|
291
|
-
noRefs: true,
|
|
292
|
-
sortKeys: false
|
|
293
|
-
});
|
|
428
|
+
return yaml.dump(secrets, { indent: 2, lineWidth: 120, noRefs: true, sortKeys: false });
|
|
294
429
|
}
|
|
295
430
|
|
|
296
431
|
module.exports = {
|