@aifabrix/builder 2.5.0 → 2.5.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/lib/commands/secure.js +5 -40
- package/lib/config.js +1 -1
- package/lib/secrets.js +3 -3
- package/lib/utils/secrets-helpers.js +1 -1
- package/lib/utils/secrets-utils.js +1 -1
- package/package.json +1 -1
- package/templates/applications/README.md.hbs +1 -1
- package/templates/infra/compose.yaml +4 -4
- package/templates/python/docker-compose.hbs +35 -20
- package/templates/typescript/docker-compose.hbs +35 -20
package/lib/commands/secure.js
CHANGED
|
@@ -22,7 +22,7 @@ const { encryptYamlValues } = require('../utils/yaml-preserve');
|
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* Finds all secrets.local.yaml files to encrypt
|
|
25
|
-
* Includes user secrets file and
|
|
25
|
+
* Includes user secrets file and general secrets from config.yaml
|
|
26
26
|
*
|
|
27
27
|
* @async
|
|
28
28
|
* @function findSecretsFiles
|
|
@@ -37,45 +37,10 @@ async function findSecretsFiles() {
|
|
|
37
37
|
files.push({ path: userSecretsPath, type: 'user' });
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
//
|
|
41
|
-
// Scan builder directory for apps
|
|
40
|
+
// Check config.yaml for aifabrix-secrets
|
|
42
41
|
try {
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
const entries = fs.readdirSync(builderDir, { withFileTypes: true });
|
|
46
|
-
for (const entry of entries) {
|
|
47
|
-
if (entry.isDirectory()) {
|
|
48
|
-
const appName = entry.name;
|
|
49
|
-
const variablesPath = path.join(builderDir, appName, 'variables.yaml');
|
|
50
|
-
if (fs.existsSync(variablesPath)) {
|
|
51
|
-
try {
|
|
52
|
-
const variablesContent = fs.readFileSync(variablesPath, 'utf8');
|
|
53
|
-
const variables = yaml.load(variablesContent);
|
|
54
|
-
|
|
55
|
-
if (variables?.build?.secrets) {
|
|
56
|
-
const buildSecretsPath = path.resolve(
|
|
57
|
-
path.dirname(variablesPath),
|
|
58
|
-
variables.build.secrets
|
|
59
|
-
);
|
|
60
|
-
if (fs.existsSync(buildSecretsPath)) {
|
|
61
|
-
files.push({ path: buildSecretsPath, type: `app:${appName}` });
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
} catch (error) {
|
|
65
|
-
// Ignore errors, continue
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
} catch (error) {
|
|
72
|
-
// Ignore errors, continue
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Check config.yaml for general secrets-path
|
|
76
|
-
try {
|
|
77
|
-
const { getSecretsPath } = require('../config');
|
|
78
|
-
const generalSecretsPath = await getSecretsPath();
|
|
42
|
+
const { getAifabrixSecretsPath } = require('../config');
|
|
43
|
+
const generalSecretsPath = await getAifabrixSecretsPath();
|
|
79
44
|
if (generalSecretsPath) {
|
|
80
45
|
const resolvedPath = path.isAbsolute(generalSecretsPath)
|
|
81
46
|
? generalSecretsPath
|
|
@@ -205,7 +170,7 @@ async function handleSecure(options) {
|
|
|
205
170
|
|
|
206
171
|
if (secretsFiles.length === 0) {
|
|
207
172
|
logger.log(chalk.yellow('⚠️ No secrets files found to encrypt'));
|
|
208
|
-
logger.log(chalk.gray(' Create ~/.aifabrix/secrets.local.yaml or configure
|
|
173
|
+
logger.log(chalk.gray(' Create ~/.aifabrix/secrets.local.yaml or configure aifabrix-secrets in config.yaml'));
|
|
209
174
|
return;
|
|
210
175
|
}
|
|
211
176
|
|
package/lib/config.js
CHANGED
|
@@ -363,7 +363,7 @@ async function setSecretsEncryptionKey(key) {
|
|
|
363
363
|
|
|
364
364
|
/**
|
|
365
365
|
* Get general secrets path from configuration
|
|
366
|
-
*
|
|
366
|
+
* Returns aifabrix-secrets path from config.yaml if configured
|
|
367
367
|
* @returns {Promise<string|null>} Secrets path or null if not set
|
|
368
368
|
*/
|
|
369
369
|
async function getSecretsPath() {
|
package/lib/secrets.js
CHANGED
|
@@ -109,13 +109,13 @@ async function decryptSecretsObject(secrets) {
|
|
|
109
109
|
/**
|
|
110
110
|
* Loads secrets with cascading lookup
|
|
111
111
|
* Supports both user secrets (~/.aifabrix/secrets.local.yaml) and project overrides
|
|
112
|
-
* User's file takes priority, then falls back to
|
|
112
|
+
* User's file takes priority, then falls back to aifabrix-secrets from config.yaml
|
|
113
113
|
* Automatically decrypts values with secure:// prefix
|
|
114
114
|
*
|
|
115
115
|
* @async
|
|
116
116
|
* @function loadSecrets
|
|
117
117
|
* @param {string} [secretsPath] - Path to secrets file (optional, for explicit override)
|
|
118
|
-
* @param {string} [appName] - Application name (optional, for
|
|
118
|
+
* @param {string} [appName] - Application name (optional, for backward compatibility)
|
|
119
119
|
* @returns {Promise<Object>} Loaded secrets object with decrypted values
|
|
120
120
|
* @throws {Error} If no secrets file found and no fallback available
|
|
121
121
|
*
|
|
@@ -158,7 +158,7 @@ async function loadSecrets(secretsPath, _appName) {
|
|
|
158
158
|
* @param {string} [environment='local'] - Environment context (docker/local)
|
|
159
159
|
* @param {Object|string|null} [secretsFilePaths] - Paths object with userPath and buildPath, or string path (for backward compatibility)
|
|
160
160
|
* @param {string} [secretsFilePaths.userPath] - User's secrets file path
|
|
161
|
-
* @param {string|null} [secretsFilePaths.buildPath] - App's
|
|
161
|
+
* @param {string|null} [secretsFilePaths.buildPath] - App's aifabrix-secrets file path (from config.yaml, if configured)
|
|
162
162
|
* @param {string} [appName] - Application name (optional, for error messages)
|
|
163
163
|
* @returns {Promise<string>} Resolved environment content
|
|
164
164
|
* @throws {Error} If kv:// reference cannot be resolved
|
|
@@ -311,7 +311,7 @@ async function applyCanonicalSecretsOverride(currentSecrets) {
|
|
|
311
311
|
*/
|
|
312
312
|
function ensureNonEmptySecrets(secrets) {
|
|
313
313
|
if (Object.keys(secrets || {}).length === 0) {
|
|
314
|
-
throw new Error('No secrets file found. Please create ~/.aifabrix/secrets.local.yaml or configure
|
|
314
|
+
throw new Error('No secrets file found. Please create ~/.aifabrix/secrets.local.yaml or configure aifabrix-secrets in config.yaml');
|
|
315
315
|
}
|
|
316
316
|
}
|
|
317
317
|
|
|
@@ -17,7 +17,7 @@ const logger = require('./logger');
|
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Loads secrets from file with cascading lookup support
|
|
20
|
-
* First checks ~/.aifabrix/secrets.local.yaml, then
|
|
20
|
+
* First checks ~/.aifabrix/secrets.local.yaml, then aifabrix-secrets from config.yaml
|
|
21
21
|
*
|
|
22
22
|
* @async
|
|
23
23
|
* @function loadSecretsFromFile
|
package/package.json
CHANGED
|
@@ -167,7 +167,7 @@ aifabrix-secrets: "/path/to/secrets.yaml"
|
|
|
167
167
|
- **"Not logged in"** → Run `aifabrix login` first
|
|
168
168
|
- **"Port already in use"** → Use `--port` flag or change `build.localPort` in `variables.yaml` (default: {{port}})
|
|
169
169
|
- **"Authentication failed"** → Run `aifabrix login` again
|
|
170
|
-
- **"Build fails"** → Check Docker is running and `
|
|
170
|
+
- **"Build fails"** → Check Docker is running and `aifabrix-secrets` in `config.yaml` is configured correctly
|
|
171
171
|
- **"Can't connect"** → Verify infrastructure is running{{#if hasDatabase}} and PostgreSQL is accessible{{/if}}
|
|
172
172
|
|
|
173
173
|
**Regenerate files:**
|
|
@@ -13,7 +13,7 @@ services:
|
|
|
13
13
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
|
14
14
|
POSTGRES_INITDB_ARGS: "--encoding=UTF-8 --lc-collate=C --lc-ctype=C"
|
|
15
15
|
ports:
|
|
16
|
-
- "5432:5432"
|
|
16
|
+
- "127.0.0.1:5432:5432"
|
|
17
17
|
volumes:
|
|
18
18
|
- postgres_data:/var/lib/postgresql/data
|
|
19
19
|
- ./init-scripts:/docker-entrypoint-initdb.d
|
|
@@ -32,7 +32,7 @@ services:
|
|
|
32
32
|
container_name: aifabrix-redis
|
|
33
33
|
command: ["redis-server", "--appendonly", "yes"]
|
|
34
34
|
ports:
|
|
35
|
-
- "6379:6379"
|
|
35
|
+
- "127.0.0.1:6379:6379"
|
|
36
36
|
volumes:
|
|
37
37
|
- redis_data:/data
|
|
38
38
|
networks:
|
|
@@ -53,7 +53,7 @@ services:
|
|
|
53
53
|
PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD}
|
|
54
54
|
PGADMIN_CONFIG_SERVER_MODE: 'False'
|
|
55
55
|
ports:
|
|
56
|
-
- "5050:80"
|
|
56
|
+
- "127.0.0.1:5050:80"
|
|
57
57
|
volumes:
|
|
58
58
|
- pgadmin_data:/var/lib/pgadmin
|
|
59
59
|
restart: unless-stopped
|
|
@@ -73,7 +73,7 @@ services:
|
|
|
73
73
|
HTTP_PASSWORD: ${REDIS_COMMANDER_PASSWORD}
|
|
74
74
|
REDIS_PASSWORD: ""
|
|
75
75
|
ports:
|
|
76
|
-
- "8081:8081"
|
|
76
|
+
- "127.0.0.1:8081:8081"
|
|
77
77
|
restart: unless-stopped
|
|
78
78
|
depends_on:
|
|
79
79
|
redis:
|
|
@@ -34,6 +34,7 @@ services:
|
|
|
34
34
|
db-init:
|
|
35
35
|
image: pgvector/pgvector:pg15
|
|
36
36
|
container_name: {{containerName}}-db-init
|
|
37
|
+
entrypoint: []
|
|
37
38
|
env_file:
|
|
38
39
|
- ${ADMIN_SECRETS_PATH}
|
|
39
40
|
- {{envFile}}
|
|
@@ -51,10 +52,15 @@ services:
|
|
|
51
52
|
{{/if}}
|
|
52
53
|
networks:
|
|
53
54
|
- {{networkName}}
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
volumes: []
|
|
56
|
+
tmpfs:
|
|
57
|
+
- /var/lib/postgresql/data
|
|
58
|
+
command:
|
|
59
|
+
- sh
|
|
60
|
+
- -c
|
|
61
|
+
- |
|
|
56
62
|
export PGHOST=postgres PGPORT=5432 PGUSER=pgadmin &&
|
|
57
|
-
export PGPASSWORD
|
|
63
|
+
export PGPASSWORD="${POSTGRES_PASSWORD}" &&
|
|
58
64
|
echo 'Waiting for PostgreSQL to be ready...' &&
|
|
59
65
|
counter=0 &&
|
|
60
66
|
while [ ${counter:-0} -lt 30 ]; do
|
|
@@ -69,32 +75,41 @@ services:
|
|
|
69
75
|
{{#if databases}}
|
|
70
76
|
{{#each databases}}
|
|
71
77
|
echo 'Creating {{name}} database and user...' &&
|
|
72
|
-
if psql -d postgres -tAc
|
|
73
|
-
echo 'Database
|
|
78
|
+
if psql -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = '{{name}}'" 2>/dev/null | grep -q '^1$'; then
|
|
79
|
+
echo 'Database "{{name}}" already exists, all ok.'
|
|
74
80
|
else
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
psql -d postgres -c
|
|
79
|
-
|
|
80
|
-
psql -d
|
|
81
|
+
echo 'Creating database "{{name}}"...' &&
|
|
82
|
+
psql -d postgres -c "CREATE DATABASE \"{{name}}\";" &&
|
|
83
|
+
echo 'Dropping old user if exists...' &&
|
|
84
|
+
psql -d postgres -c "DROP USER IF EXISTS \"{{pgUserOld name}}\";" || true &&
|
|
85
|
+
echo 'Creating user "{{pgUserName name}}"...' &&
|
|
86
|
+
psql -d postgres -c 'CREATE USER "{{pgUserName name}}" WITH PASSWORD '\''${DB_{{@index}}_PASSWORD}'\'';' &&
|
|
87
|
+
echo 'Granting privileges...' &&
|
|
88
|
+
psql -d postgres -c "GRANT ALL PRIVILEGES ON DATABASE \"{{name}}\" TO \"{{pgUserName name}}\";" &&
|
|
89
|
+
psql -d {{name}} -c "ALTER SCHEMA public OWNER TO \"{{pgUserName name}}\";" &&
|
|
90
|
+
psql -d {{name}} -c "GRANT ALL ON SCHEMA public TO \"{{pgUserName name}}\";" &&
|
|
91
|
+
echo 'Database "{{name}}" created successfully!'
|
|
81
92
|
fi &&
|
|
82
93
|
{{/each}}
|
|
83
94
|
{{else}}
|
|
84
95
|
echo 'Creating {{app.key}} database and user...' &&
|
|
85
|
-
if psql -d postgres -tAc
|
|
86
|
-
echo 'Database
|
|
96
|
+
if psql -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = '{{app.key}}'" 2>/dev/null | grep -q '^1$'; then
|
|
97
|
+
echo 'Database "{{app.key}}" already exists, all ok.'
|
|
87
98
|
else
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
psql -d postgres -c
|
|
92
|
-
|
|
93
|
-
psql -d
|
|
99
|
+
echo 'Creating database "{{app.key}}"...' &&
|
|
100
|
+
psql -d postgres -c "CREATE DATABASE \"{{app.key}}\";" &&
|
|
101
|
+
echo 'Dropping old user if exists...' &&
|
|
102
|
+
psql -d postgres -c "DROP USER IF EXISTS \"{{pgUserOld app.key}}\";" || true &&
|
|
103
|
+
echo 'Creating user "{{pgUserName app.key}}"...' &&
|
|
104
|
+
psql -d postgres -c 'CREATE USER "{{pgUserName app.key}}" WITH PASSWORD '\''${DB_0_PASSWORD:-${DB_PASSWORD}}'\'';' &&
|
|
105
|
+
echo 'Granting privileges...' &&
|
|
106
|
+
psql -d postgres -c "GRANT ALL PRIVILEGES ON DATABASE \"{{app.key}}\" TO \"{{pgUserName app.key}}\";" &&
|
|
107
|
+
psql -d {{app.key}} -c "ALTER SCHEMA public OWNER TO \"{{pgUserName app.key}}\";" &&
|
|
108
|
+
psql -d {{app.key}} -c "GRANT ALL ON SCHEMA public TO \"{{pgUserName app.key}}\";" &&
|
|
109
|
+
echo 'Database "{{app.key}}" created successfully!'
|
|
94
110
|
fi &&
|
|
95
111
|
{{/if}}
|
|
96
112
|
echo 'Database initialization complete!'
|
|
97
|
-
"
|
|
98
113
|
restart: "no"
|
|
99
114
|
{{/if}}
|
|
100
115
|
|
|
@@ -34,6 +34,7 @@ services:
|
|
|
34
34
|
db-init:
|
|
35
35
|
image: pgvector/pgvector:pg15
|
|
36
36
|
container_name: {{containerName}}-db-init
|
|
37
|
+
entrypoint: []
|
|
37
38
|
env_file:
|
|
38
39
|
- ${ADMIN_SECRETS_PATH}
|
|
39
40
|
- {{envFile}}
|
|
@@ -51,10 +52,15 @@ services:
|
|
|
51
52
|
{{/if}}
|
|
52
53
|
networks:
|
|
53
54
|
- {{networkName}}
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
volumes: []
|
|
56
|
+
tmpfs:
|
|
57
|
+
- /var/lib/postgresql/data
|
|
58
|
+
command:
|
|
59
|
+
- sh
|
|
60
|
+
- -c
|
|
61
|
+
- |
|
|
56
62
|
export PGHOST=postgres PGPORT=5432 PGUSER=pgadmin &&
|
|
57
|
-
export PGPASSWORD
|
|
63
|
+
export PGPASSWORD="${POSTGRES_PASSWORD}" &&
|
|
58
64
|
echo 'Waiting for PostgreSQL to be ready...' &&
|
|
59
65
|
counter=0 &&
|
|
60
66
|
while [ ${counter:-0} -lt 30 ]; do
|
|
@@ -69,32 +75,41 @@ services:
|
|
|
69
75
|
{{#if databases}}
|
|
70
76
|
{{#each databases}}
|
|
71
77
|
echo 'Creating {{name}} database and user...' &&
|
|
72
|
-
if psql -d postgres -tAc
|
|
73
|
-
echo 'Database
|
|
78
|
+
if psql -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = '{{name}}'" 2>/dev/null | grep -q '^1$'; then
|
|
79
|
+
echo 'Database "{{name}}" already exists, all ok.'
|
|
74
80
|
else
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
psql -d postgres -c
|
|
79
|
-
|
|
80
|
-
psql -d
|
|
81
|
+
echo 'Creating database "{{name}}"...' &&
|
|
82
|
+
psql -d postgres -c "CREATE DATABASE \"{{name}}\";" &&
|
|
83
|
+
echo 'Dropping old user if exists...' &&
|
|
84
|
+
psql -d postgres -c "DROP USER IF EXISTS \"{{pgUserOld name}}\";" || true &&
|
|
85
|
+
echo 'Creating user "{{pgUserName name}}"...' &&
|
|
86
|
+
psql -d postgres -c 'CREATE USER "{{pgUserName name}}" WITH PASSWORD '\''${DB_{{@index}}_PASSWORD}'\'';' &&
|
|
87
|
+
echo 'Granting privileges...' &&
|
|
88
|
+
psql -d postgres -c "GRANT ALL PRIVILEGES ON DATABASE \"{{name}}\" TO \"{{pgUserName name}}\";" &&
|
|
89
|
+
psql -d {{name}} -c "ALTER SCHEMA public OWNER TO \"{{pgUserName name}}\";" &&
|
|
90
|
+
psql -d {{name}} -c "GRANT ALL ON SCHEMA public TO \"{{pgUserName name}}\";" &&
|
|
91
|
+
echo 'Database "{{name}}" created successfully!'
|
|
81
92
|
fi &&
|
|
82
93
|
{{/each}}
|
|
83
94
|
{{else}}
|
|
84
95
|
echo 'Creating {{app.key}} database and user...' &&
|
|
85
|
-
if psql -d postgres -tAc
|
|
86
|
-
echo 'Database
|
|
96
|
+
if psql -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = '{{app.key}}'" 2>/dev/null | grep -q '^1$'; then
|
|
97
|
+
echo 'Database "{{app.key}}" already exists, all ok.'
|
|
87
98
|
else
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
psql -d postgres -c
|
|
92
|
-
|
|
93
|
-
psql -d
|
|
99
|
+
echo 'Creating database "{{app.key}}"...' &&
|
|
100
|
+
psql -d postgres -c "CREATE DATABASE \"{{app.key}}\";" &&
|
|
101
|
+
echo 'Dropping old user if exists...' &&
|
|
102
|
+
psql -d postgres -c "DROP USER IF EXISTS \"{{pgUserOld app.key}}\";" || true &&
|
|
103
|
+
echo 'Creating user "{{pgUserName app.key}}"...' &&
|
|
104
|
+
psql -d postgres -c 'CREATE USER "{{pgUserName app.key}}" WITH PASSWORD '\''${DB_0_PASSWORD:-${DB_PASSWORD}}'\'';' &&
|
|
105
|
+
echo 'Granting privileges...' &&
|
|
106
|
+
psql -d postgres -c "GRANT ALL PRIVILEGES ON DATABASE \"{{app.key}}\" TO \"{{pgUserName app.key}}\";" &&
|
|
107
|
+
psql -d {{app.key}} -c "ALTER SCHEMA public OWNER TO \"{{pgUserName app.key}}\";" &&
|
|
108
|
+
psql -d {{app.key}} -c "GRANT ALL ON SCHEMA public TO \"{{pgUserName app.key}}\";" &&
|
|
109
|
+
echo 'Database "{{app.key}}" created successfully!'
|
|
94
110
|
fi &&
|
|
95
111
|
{{/if}}
|
|
96
112
|
echo 'Database initialization complete!'
|
|
97
|
-
"
|
|
98
113
|
restart: "no"
|
|
99
114
|
{{/if}}
|
|
100
115
|
|