@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.
Files changed (58) hide show
  1. package/README.md +6 -2
  2. package/bin/aifabrix.js +9 -3
  3. package/jest.config.integration.js +30 -0
  4. package/lib/app-config.js +157 -0
  5. package/lib/app-deploy.js +233 -82
  6. package/lib/app-dockerfile.js +112 -0
  7. package/lib/app-prompts.js +244 -0
  8. package/lib/app-push.js +172 -0
  9. package/lib/app-run.js +334 -133
  10. package/lib/app.js +208 -274
  11. package/lib/audit-logger.js +2 -0
  12. package/lib/build.js +209 -98
  13. package/lib/cli.js +76 -86
  14. package/lib/commands/app.js +414 -0
  15. package/lib/commands/login.js +304 -0
  16. package/lib/config.js +78 -0
  17. package/lib/deployer.js +225 -81
  18. package/lib/env-reader.js +45 -30
  19. package/lib/generator.js +308 -191
  20. package/lib/github-generator.js +67 -7
  21. package/lib/infra.js +156 -61
  22. package/lib/push.js +105 -10
  23. package/lib/schema/application-schema.json +30 -2
  24. package/lib/schema/infrastructure-schema.json +589 -0
  25. package/lib/secrets.js +229 -24
  26. package/lib/template-validator.js +205 -0
  27. package/lib/templates.js +305 -170
  28. package/lib/utils/api.js +329 -0
  29. package/lib/utils/cli-utils.js +97 -0
  30. package/lib/utils/dockerfile-utils.js +131 -0
  31. package/lib/utils/environment-checker.js +125 -0
  32. package/lib/utils/error-formatter.js +61 -0
  33. package/lib/utils/health-check.js +187 -0
  34. package/lib/utils/logger.js +53 -0
  35. package/lib/utils/template-helpers.js +223 -0
  36. package/lib/utils/variable-transformer.js +271 -0
  37. package/lib/validator.js +27 -112
  38. package/package.json +13 -10
  39. package/templates/README.md +75 -3
  40. package/templates/applications/keycloak/Dockerfile +36 -0
  41. package/templates/applications/keycloak/env.template +32 -0
  42. package/templates/applications/keycloak/rbac.yaml +37 -0
  43. package/templates/applications/keycloak/variables.yaml +56 -0
  44. package/templates/applications/miso-controller/Dockerfile +125 -0
  45. package/templates/applications/miso-controller/env.template +129 -0
  46. package/templates/applications/miso-controller/rbac.yaml +168 -0
  47. package/templates/applications/miso-controller/variables.yaml +56 -0
  48. package/templates/github/release.yaml.hbs +5 -26
  49. package/templates/github/steps/npm.hbs +24 -0
  50. package/templates/infra/compose.yaml +6 -6
  51. package/templates/python/docker-compose.hbs +19 -12
  52. package/templates/python/main.py +80 -0
  53. package/templates/python/requirements.txt +4 -0
  54. package/templates/typescript/Dockerfile.hbs +2 -2
  55. package/templates/typescript/docker-compose.hbs +19 -12
  56. package/templates/typescript/index.ts +116 -0
  57. package/templates/typescript/package.json +26 -0
  58. package/templates/typescript/tsconfig.json +24 -0
@@ -8,11 +8,12 @@ services:
8
8
  {{app.key}}:
9
9
  image: {{image.name}}:{{image.tag}}
10
10
  container_name: aifabrix-{{app.key}}
11
- env_file: .env
11
+ env_file:
12
+ - {{envFile}}
12
13
  ports:
13
14
  - "{{build.localPort}}:{{port}}"
14
15
  networks:
15
- - aifabrix-network
16
+ - infra_aifabrix-network
16
17
  {{#if requiresStorage}}
17
18
  volumes:
18
19
  - "{{mountVolume}}:/mnt/data"
@@ -39,31 +40,37 @@ services:
39
40
  - ${ADMIN_SECRETS_PATH}
40
41
  environment:
41
42
  POSTGRES_DB: postgres
43
+ PGHOST: postgres
44
+ PGPORT: "5432"
45
+ PGUSER: pgadmin
42
46
  networks:
43
- - aifabrix-network
47
+ - infra_aifabrix-network
44
48
  command: >
45
49
  sh -c "
46
- echo 'Creating {{app.key}} database and user...' &&
47
- psql -d postgres -c 'CREATE DATABASE {{app.key}};' || echo '{{app.key}} database exists' &&
48
- psql -d postgres -c \"CREATE USER {{app.key}}_user WITH PASSWORD '{{app.key}}_pass123';\" || echo '{{app.key}}_user exists' &&
49
- psql -d postgres -c 'GRANT ALL PRIVILEGES ON DATABASE {{app.key}} TO {{app.key}}_user;' &&
50
- psql -d {{app.key}} -c 'ALTER SCHEMA public OWNER TO {{app.key}}_user;' &&
51
- psql -d {{app.key}} -c 'GRANT ALL ON SCHEMA public TO {{app.key}}_user;' &&
50
+ export PGHOST=postgres PGPORT=5432 PGUSER=pgadmin &&
51
+ export PGPASSWORD="${POSTGRES_PASSWORD}" &&
52
+ {{#if databases}}
52
53
  {{#each databases}}
53
- {{#unless @first}}
54
54
  echo 'Creating {{name}} database and user...' &&
55
55
  psql -d postgres -c 'CREATE DATABASE {{name}};' || echo '{{name}} database exists' &&
56
56
  psql -d postgres -c \"CREATE USER {{name}}_user WITH PASSWORD '{{name}}_pass123';\" || echo '{{name}}_user exists' &&
57
57
  psql -d postgres -c 'GRANT ALL PRIVILEGES ON DATABASE {{name}} TO {{name}}_user;' &&
58
58
  psql -d {{name}} -c 'ALTER SCHEMA public OWNER TO {{name}}_user;' &&
59
59
  psql -d {{name}} -c 'GRANT ALL ON SCHEMA public TO {{name}}_user;' &&
60
- {{/unless}}
61
60
  {{/each}}
61
+ {{else}}
62
+ echo 'Creating {{app.key}} database and user...' &&
63
+ psql -d postgres -c 'CREATE DATABASE {{app.key}};' || echo '{{app.key}} database exists' &&
64
+ psql -d postgres -c \"CREATE USER {{app.key}}_user WITH PASSWORD '{{app.key}}_pass123';\" || echo '{{app.key}}_user exists' &&
65
+ psql -d postgres -c 'GRANT ALL PRIVILEGES ON DATABASE {{app.key}} TO {{app.key}}_user;' &&
66
+ psql -d {{app.key}} -c 'ALTER SCHEMA public OWNER TO {{app.key}}_user;' &&
67
+ psql -d {{app.key}} -c 'GRANT ALL ON SCHEMA public TO {{app.key}}_user;' &&
68
+ {{/if}}
62
69
  echo 'Database initialization complete!'
63
70
  "
64
71
  restart: "no"
65
72
  {{/if}}
66
73
 
67
74
  networks:
68
- aifabrix-network:
75
+ infra_aifabrix-network:
69
76
  external: true
@@ -0,0 +1,80 @@
1
+ import os
2
+ from datetime import datetime
3
+ from flask import Flask, jsonify
4
+
5
+ app = Flask(__name__)
6
+ PORT = int(os.environ.get('PORT', 3000))
7
+
8
+ def check_database():
9
+ """Check database connection"""
10
+ database_url = os.environ.get('DATABASE_URL')
11
+
12
+ try:
13
+ import psycopg2
14
+
15
+ # If DATABASE_URL is set, use it
16
+ if database_url:
17
+ from urllib.parse import urlparse
18
+ # Parse DATABASE_URL (format: postgresql://user:password@host:port/database)
19
+ parsed = urlparse(database_url)
20
+
21
+ conn = psycopg2.connect(
22
+ host=parsed.hostname or os.environ.get('DATABASE_HOST', 'postgres'),
23
+ port=parsed.port or int(os.environ.get('DATABASE_PORT', 5432)),
24
+ database=parsed.path[1:] if parsed.path else os.environ.get('DATABASE_NAME', 'postgres'),
25
+ user=parsed.username or os.environ.get('DATABASE_USER', 'pgadmin'),
26
+ password=parsed.password or os.environ.get('DATABASE_PASSWORD', 'admin123')
27
+ )
28
+ else:
29
+ # Fallback to individual environment variables
30
+ conn = psycopg2.connect(
31
+ host=os.environ.get('DATABASE_HOST', os.environ.get('DB_HOST', 'postgres')),
32
+ port=int(os.environ.get('DATABASE_PORT', os.environ.get('DB_PORT', 5432))),
33
+ database=os.environ.get('DATABASE_NAME', os.environ.get('DB_NAME', 'postgres')),
34
+ user=os.environ.get('DATABASE_USER', os.environ.get('DB_USER', 'pgadmin')),
35
+ password=os.environ.get('DATABASE_PASSWORD', os.environ.get('DB_PASSWORD', 'admin123'))
36
+ )
37
+
38
+ conn.close()
39
+ return True
40
+ except ImportError:
41
+ return 'psycopg2 not installed'
42
+ except Exception as e:
43
+ return str(e)
44
+
45
+ @app.route('/health', methods=['GET'])
46
+ def health():
47
+ """Health check endpoint with database connectivity check"""
48
+ health_status = {
49
+ 'status': 'ok',
50
+ 'timestamp': datetime.utcnow().isoformat() + 'Z'
51
+ }
52
+
53
+ # Check database connection if database is configured (DATABASE_URL or individual vars)
54
+ database_url = os.environ.get('DATABASE_URL')
55
+ database_host = os.environ.get('DATABASE_HOST') or os.environ.get('DB_HOST')
56
+ database_name = os.environ.get('DATABASE_NAME') or os.environ.get('DB_NAME')
57
+
58
+ # Only check database if database is configured
59
+ if database_url or database_host or database_name:
60
+ db_check = check_database()
61
+ if db_check is True:
62
+ health_status['database'] = 'connected'
63
+ else:
64
+ health_status['database'] = 'error'
65
+ health_status['database_error'] = str(db_check)
66
+ return jsonify(health_status), 503
67
+
68
+ return jsonify(health_status), 200
69
+
70
+ @app.route('/', methods=['GET'])
71
+ def root():
72
+ """Root endpoint"""
73
+ return jsonify({
74
+ 'message': 'AI Fabrix Application',
75
+ 'version': '1.0.0'
76
+ }), 200
77
+
78
+ if __name__ == '__main__':
79
+ app.run(host='0.0.0.0', port=PORT, debug=False)
80
+
@@ -0,0 +1,4 @@
1
+ Flask==3.0.0
2
+ gunicorn==21.2.0
3
+ psycopg2-binary==2.9.9
4
+
@@ -16,8 +16,8 @@ RUN apk add --no-cache \
16
16
  # Copy package files first for better layer caching
17
17
  COPY package*.json ./
18
18
 
19
- # Install dependencies
20
- RUN npm ci --only=production && npm cache clean --force
19
+ # Install dependencies (including devDependencies for ts-node)
20
+ RUN npm install && npm cache clean --force
21
21
 
22
22
  # Copy application code
23
23
  COPY . .
@@ -8,11 +8,12 @@ services:
8
8
  {{app.key}}:
9
9
  image: {{image.name}}:{{image.tag}}
10
10
  container_name: aifabrix-{{app.key}}
11
- env_file: .env
11
+ env_file:
12
+ - {{envFile}}
12
13
  ports:
13
14
  - "{{build.localPort}}:{{port}}"
14
15
  networks:
15
- - aifabrix-network
16
+ - infra_aifabrix-network
16
17
  {{#if requiresStorage}}
17
18
  volumes:
18
19
  - "{{mountVolume}}:/mnt/data"
@@ -39,31 +40,37 @@ services:
39
40
  - ${ADMIN_SECRETS_PATH}
40
41
  environment:
41
42
  POSTGRES_DB: postgres
43
+ PGHOST: postgres
44
+ PGPORT: "5432"
45
+ PGUSER: pgadmin
42
46
  networks:
43
- - aifabrix-network
47
+ - infra_aifabrix-network
44
48
  command: >
45
49
  sh -c "
46
- echo 'Creating {{app.key}} database and user...' &&
47
- psql -d postgres -c 'CREATE DATABASE {{app.key}};' || echo '{{app.key}} database exists' &&
48
- psql -d postgres -c \"CREATE USER {{app.key}}_user WITH PASSWORD '{{app.key}}_pass123';\" || echo '{{app.key}}_user exists' &&
49
- psql -d postgres -c 'GRANT ALL PRIVILEGES ON DATABASE {{app.key}} TO {{app.key}}_user;' &&
50
- psql -d {{app.key}} -c 'ALTER SCHEMA public OWNER TO {{app.key}}_user;' &&
51
- psql -d {{app.key}} -c 'GRANT ALL ON SCHEMA public TO {{app.key}}_user;' &&
50
+ export PGHOST=postgres PGPORT=5432 PGUSER=pgadmin &&
51
+ export PGPASSWORD="${POSTGRES_PASSWORD}" &&
52
+ {{#if databases}}
52
53
  {{#each databases}}
53
- {{#unless @first}}
54
54
  echo 'Creating {{name}} database and user...' &&
55
55
  psql -d postgres -c 'CREATE DATABASE {{name}};' || echo '{{name}} database exists' &&
56
56
  psql -d postgres -c \"CREATE USER {{name}}_user WITH PASSWORD '{{name}}_pass123';\" || echo '{{name}}_user exists' &&
57
57
  psql -d postgres -c 'GRANT ALL PRIVILEGES ON DATABASE {{name}} TO {{name}}_user;' &&
58
58
  psql -d {{name}} -c 'ALTER SCHEMA public OWNER TO {{name}}_user;' &&
59
59
  psql -d {{name}} -c 'GRANT ALL ON SCHEMA public TO {{name}}_user;' &&
60
- {{/unless}}
61
60
  {{/each}}
61
+ {{else}}
62
+ echo 'Creating {{app.key}} database and user...' &&
63
+ psql -d postgres -c 'CREATE DATABASE {{app.key}};' || echo '{{app.key}} database exists' &&
64
+ psql -d postgres -c \"CREATE USER {{app.key}}_user WITH PASSWORD '{{app.key}}_pass123';\" || echo '{{app.key}}_user exists' &&
65
+ psql -d postgres -c 'GRANT ALL PRIVILEGES ON DATABASE {{app.key}} TO {{app.key}}_user;' &&
66
+ psql -d {{app.key}} -c 'ALTER SCHEMA public OWNER TO {{app.key}}_user;' &&
67
+ psql -d {{app.key}} -c 'GRANT ALL ON SCHEMA public TO {{app.key}}_user;' &&
68
+ {{/if}}
62
69
  echo 'Database initialization complete!'
63
70
  "
64
71
  restart: "no"
65
72
  {{/if}}
66
73
 
67
74
  networks:
68
- aifabrix-network:
75
+ infra_aifabrix-network:
69
76
  external: true
@@ -0,0 +1,116 @@
1
+ import express from 'express';
2
+ import { Pool } from 'pg';
3
+ import { Request, Response } from 'express';
4
+
5
+ const app = express();
6
+ const PORT = process.env.PORT || 3000;
7
+
8
+ app.use(express.json());
9
+
10
+ // Database connection pool
11
+ let dbPool: Pool | null = null;
12
+
13
+ function initDatabase() {
14
+ const databaseUrl = process.env.DATABASE_URL;
15
+ if (!databaseUrl) {
16
+ return null;
17
+ }
18
+
19
+ try {
20
+ dbPool = new Pool({
21
+ connectionString: databaseUrl,
22
+ connectionTimeoutMillis: 5000,
23
+ });
24
+ return dbPool;
25
+ } catch (error) {
26
+ console.error('Failed to initialize database pool:', error);
27
+ return null;
28
+ }
29
+ }
30
+
31
+ async function checkDatabase(): Promise<{ connected: boolean; error?: string }> {
32
+ // If DATABASE_URL is not set, try to connect using individual environment variables
33
+ if (!dbPool && process.env.DATABASE_URL) {
34
+ // DATABASE_URL was set but pool initialization failed
35
+ return { connected: false, error: 'Database pool not initialized' };
36
+ }
37
+
38
+ // If no DATABASE_URL, try to create connection from individual vars
39
+ if (!dbPool) {
40
+ const host = process.env.DATABASE_HOST || process.env.DB_HOST || 'postgres';
41
+ const port = parseInt(process.env.DATABASE_PORT || process.env.DB_PORT || '5432', 10);
42
+ const database = process.env.DATABASE_NAME || process.env.DB_NAME || 'postgres';
43
+ const user = process.env.DATABASE_USER || process.env.DB_USER || 'pgadmin';
44
+ const password = process.env.DATABASE_PASSWORD || process.env.DB_PASSWORD || 'admin123';
45
+
46
+ if (!host && !database) {
47
+ return { connected: false, error: 'Database not configured' };
48
+ }
49
+
50
+ try {
51
+ const tempPool = new Pool({
52
+ host,
53
+ port,
54
+ database,
55
+ user,
56
+ password,
57
+ connectionTimeoutMillis: 5000,
58
+ });
59
+ const client = await tempPool.connect();
60
+ await client.query('SELECT 1');
61
+ client.release();
62
+ await tempPool.end();
63
+ return { connected: true };
64
+ } catch (error: any) {
65
+ return { connected: false, error: error.message || 'Database connection failed' };
66
+ }
67
+ }
68
+
69
+ try {
70
+ const client = await dbPool.connect();
71
+ await client.query('SELECT 1');
72
+ client.release();
73
+ return { connected: true };
74
+ } catch (error: any) {
75
+ return { connected: false, error: error.message || 'Database connection failed' };
76
+ }
77
+ }
78
+
79
+ // Initialize database on startup
80
+ initDatabase();
81
+
82
+ // Health check endpoint with database connectivity check
83
+ app.get('/health', async (req: Request, res: Response) => {
84
+ const healthStatus: any = {
85
+ status: 'ok',
86
+ timestamp: new Date().toISOString()
87
+ };
88
+
89
+ // Check database connection if database is configured (DATABASE_URL or individual vars)
90
+ const databaseUrl = process.env.DATABASE_URL;
91
+ const databaseHost = process.env.DATABASE_HOST || process.env.DB_HOST;
92
+ const databaseName = process.env.DATABASE_NAME || process.env.DB_NAME;
93
+
94
+ // Only check database if database is configured
95
+ if (databaseUrl || databaseHost || databaseName) {
96
+ const dbCheck = await checkDatabase();
97
+ if (dbCheck.connected) {
98
+ healthStatus.database = 'connected';
99
+ } else {
100
+ healthStatus.database = 'error';
101
+ healthStatus.database_error = dbCheck.error;
102
+ return res.status(503).json(healthStatus);
103
+ }
104
+ }
105
+
106
+ res.status(200).json(healthStatus);
107
+ });
108
+
109
+ // Root endpoint
110
+ app.get('/', (req: Request, res: Response) => {
111
+ res.json({ message: 'AI Fabrix Application', version: '1.0.0' });
112
+ });
113
+
114
+ app.listen(PORT, () => {
115
+ console.log(`Server running on port ${PORT}`);
116
+ });
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "aifabrix-app",
3
+ "version": "1.0.0",
4
+ "description": "AI Fabrix application",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "start": "ts-node --transpile-only index.ts",
8
+ "build": "tsc",
9
+ "dev": "ts-node --transpile-only index.ts"
10
+ },
11
+ "dependencies": {
12
+ "express": "^4.18.2",
13
+ "pg": "^8.11.3"
14
+ },
15
+ "devDependencies": {
16
+ "@types/express": "^4.17.21",
17
+ "@types/node": "^20.10.0",
18
+ "@types/pg": "^8.10.9",
19
+ "ts-node": "^10.9.2",
20
+ "typescript": "^5.3.3"
21
+ },
22
+ "engines": {
23
+ "node": ">=18.0.0"
24
+ }
25
+ }
26
+
@@ -0,0 +1,24 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "commonjs",
5
+ "lib": ["ES2020"],
6
+ "outDir": "./dist",
7
+ "rootDir": "./",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "resolveJsonModule": true,
13
+ "moduleResolution": "node",
14
+ "types": ["node"]
15
+ },
16
+ "ts-node": {
17
+ "transpileOnly": true,
18
+ "compilerOptions": {
19
+ "module": "commonjs"
20
+ }
21
+ },
22
+ "include": ["*.ts"],
23
+ "exclude": ["node_modules", "dist"]
24
+ }