@ductape/cli 0.2.0 → 0.2.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 (44) hide show
  1. package/.env.ductape.example +5 -0
  2. package/DB_MIGRATE_PLAN.md +223 -0
  3. package/dist/commands/apply.d.ts +6 -0
  4. package/dist/commands/apply.js +71 -0
  5. package/dist/commands/db-migrate.d.ts +19 -0
  6. package/dist/commands/db-migrate.js +154 -0
  7. package/dist/commands/db-schema.d.ts +6 -0
  8. package/dist/commands/db-schema.js +135 -0
  9. package/dist/commands/install.d.ts +1 -1
  10. package/dist/commands/install.js +47 -23
  11. package/dist/commands/platform.js +1 -1
  12. package/dist/index.js +68 -14
  13. package/dist/lib/apply-loaders.d.ts +6 -0
  14. package/dist/lib/apply-loaders.js +24 -0
  15. package/dist/lib/config.d.ts +2 -1
  16. package/dist/lib/config.js +9 -4
  17. package/dist/lib/db-types.d.ts +71 -0
  18. package/dist/lib/db-types.js +1 -0
  19. package/dist/lib/migration-files.d.ts +4 -0
  20. package/dist/lib/migration-files.js +43 -0
  21. package/dist/lib/platform-api.js +15 -1
  22. package/dist/lib/resources.js +3 -1
  23. package/dist/lib/schema-loader.d.ts +30 -0
  24. package/dist/lib/schema-loader.js +117 -0
  25. package/dist/lib/templates.js +75 -1
  26. package/ductape.example.ts +18 -0
  27. package/package.json +1 -1
  28. package/src/commands/apply.ts +105 -0
  29. package/src/commands/db-migrate.ts +211 -0
  30. package/src/commands/db-schema.ts +163 -0
  31. package/src/commands/install.ts +52 -24
  32. package/src/commands/platform.ts +1 -1
  33. package/src/index.ts +78 -16
  34. package/src/lib/apply-loaders.ts +30 -0
  35. package/src/lib/config.ts +9 -4
  36. package/src/lib/db-types.ts +104 -0
  37. package/src/lib/migration-files.ts +58 -0
  38. package/src/lib/platform-api.ts +15 -1
  39. package/src/lib/resources.ts +3 -1
  40. package/src/lib/schema-loader.ts +152 -0
  41. package/src/lib/templates.ts +80 -1
  42. package/templates/api-gateway.conf +178 -0
  43. package/templates/docker-compose.release.yml +265 -0
  44. package/templates/platform.env +16 -0
@@ -49,7 +49,7 @@ DUCTAPE_ENV=dev
49
49
  const envPath = path.join(dir, '.env.ductape.example');
50
50
  if (!fs.existsSync(envPath)) fs.writeFileSync(envPath, envContent);
51
51
 
52
- const ductapeDir = path.join(dir, '.ductape');
52
+ const ductapeDir = path.join(dir, 'ductape');
53
53
  fs.mkdirSync(ductapeDir, { recursive: true });
54
54
 
55
55
  const readme = `# Ductape project
@@ -66,6 +66,85 @@ Docs: https://docs.ductape.app/docs/cli/
66
66
  const readmePath = path.join(ductapeDir, 'README.md');
67
67
  if (!fs.existsSync(readmePath)) fs.writeFileSync(readmePath, readme);
68
68
 
69
+ const dbDir = path.join(ductapeDir, 'database');
70
+ const migrationsDir = path.join(dbDir, 'migrations');
71
+ fs.mkdirSync(migrationsDir, { recursive: true });
72
+
73
+ const schemaPath = path.join(dbDir, 'schema.json');
74
+ if (!fs.existsSync(schemaPath)) {
75
+ const schemaTemplate = [
76
+ {
77
+ db: 'your_db_tag',
78
+ tables: {
79
+ example_table: {
80
+ id: { type: 'String', primaryKey: true, autoGenerate: true },
81
+ name: { type: 'String', required: true },
82
+ createdAt: { type: 'Date', default: 'now' },
83
+ },
84
+ },
85
+ },
86
+ ];
87
+ fs.writeFileSync(schemaPath, JSON.stringify(schemaTemplate, null, 2) + '\n');
88
+ }
89
+
90
+ const sessionsPath = path.join(ductapeDir, 'sessions.json');
91
+ if (!fs.existsSync(sessionsPath)) {
92
+ const sessionsTemplate = [
93
+ {
94
+ tag: 'user-session',
95
+ name: 'User session',
96
+ description: 'Standard authenticated user session',
97
+ expiry: 604800,
98
+ refresh_expiry: 2592000,
99
+ },
100
+ ];
101
+ fs.writeFileSync(sessionsPath, JSON.stringify(sessionsTemplate, null, 2) + '\n');
102
+ }
103
+
104
+ const notificationsPath = path.join(ductapeDir, 'notifications.json');
105
+ if (!fs.existsSync(notificationsPath)) {
106
+ const notificationsTemplate = [
107
+ {
108
+ tag: 'transactional-email',
109
+ name: 'Transactional email',
110
+ description: 'Email channel for transactional messages',
111
+ envs: [
112
+ {
113
+ slug: 'dev',
114
+ emails: {
115
+ host: 'smtp.example.com',
116
+ port: 587,
117
+ user: 'your-smtp-user',
118
+ pass: 'your-smtp-pass',
119
+ sender: 'no-reply@example.com',
120
+ },
121
+ },
122
+ ],
123
+ },
124
+ ];
125
+ fs.writeFileSync(notificationsPath, JSON.stringify(notificationsTemplate, null, 2) + '\n');
126
+ }
127
+
128
+ const eventsPath = path.join(ductapeDir, 'events.json');
129
+ if (!fs.existsSync(eventsPath)) {
130
+ const eventsTemplate = [
131
+ {
132
+ tag: 'main-event-bus',
133
+ name: 'Main event bus',
134
+ description: 'Primary message broker for application events',
135
+ envs: [
136
+ {
137
+ slug: 'dev',
138
+ type: 'kafka',
139
+ brokers: ['localhost:9092'],
140
+ client_id: 'my-app-dev',
141
+ },
142
+ ],
143
+ },
144
+ ];
145
+ fs.writeFileSync(eventsPath, JSON.stringify(eventsTemplate, null, 2) + '\n');
146
+ }
147
+
69
148
  copyTemplate(lang, dir);
70
149
 
71
150
  const gitignorePath = path.join(dir, '.gitignore');
@@ -0,0 +1,178 @@
1
+ upstream nest_users { server users:8002; }
2
+ upstream nest_workspaces { server workspaces:8001; }
3
+ upstream nest_emails { server emails:8003; }
4
+ upstream nest_apps { server apps:8004; }
5
+ upstream nest_marketplace { server marketplace:8007; }
6
+ upstream nest_logs { server logs:8008; }
7
+ upstream nest_integrations { server integrations:8009; }
8
+ upstream nest_webhooks { server webhooks:8012; }
9
+ upstream nest_notifications { server notifications:8013; }
10
+ upstream nest_partnerships { server partnerships:8014; }
11
+ upstream nest_jobs { server jobs:8015; }
12
+ upstream nest_pricing { server pricing:8011; }
13
+ upstream nest_admins { server admins:8018; }
14
+ upstream nest_proxy { server proxy:8020; }
15
+ upstream nest_tickets { server tickets:9002; }
16
+ upstream nest_realtime { server realtime:3030; }
17
+
18
+ server {
19
+ listen 8000;
20
+ client_max_body_size 50m;
21
+
22
+ location /users/ {
23
+ proxy_pass http://nest_users/users/;
24
+ proxy_http_version 1.1;
25
+ proxy_set_header Host $host;
26
+ proxy_set_header X-Real-IP $remote_addr;
27
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
28
+ proxy_set_header X-Forwarded-Proto $scheme;
29
+ }
30
+
31
+ location /workspaces/ {
32
+ proxy_pass http://nest_workspaces/workspaces/;
33
+ proxy_http_version 1.1;
34
+ proxy_set_header Host $host;
35
+ proxy_set_header X-Real-IP $remote_addr;
36
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
37
+ proxy_set_header X-Forwarded-Proto $scheme;
38
+ }
39
+
40
+ location /emails/ {
41
+ proxy_pass http://nest_emails/emails/;
42
+ proxy_http_version 1.1;
43
+ proxy_set_header Host $host;
44
+ proxy_set_header X-Real-IP $remote_addr;
45
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
46
+ proxy_set_header X-Forwarded-Proto $scheme;
47
+ }
48
+
49
+ location /apps/ {
50
+ proxy_pass http://nest_apps/apps/;
51
+ proxy_http_version 1.1;
52
+ proxy_set_header Host $host;
53
+ proxy_set_header X-Real-IP $remote_addr;
54
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
55
+ proxy_set_header X-Forwarded-Proto $scheme;
56
+ }
57
+
58
+ location /marketplace/ {
59
+ proxy_pass http://nest_marketplace/marketplace/;
60
+ proxy_http_version 1.1;
61
+ proxy_set_header Host $host;
62
+ proxy_set_header X-Real-IP $remote_addr;
63
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
64
+ proxy_set_header X-Forwarded-Proto $scheme;
65
+ }
66
+
67
+ location /logs/ {
68
+ proxy_pass http://nest_logs/logs/;
69
+ proxy_http_version 1.1;
70
+ proxy_set_header Host $host;
71
+ proxy_set_header X-Real-IP $remote_addr;
72
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
73
+ proxy_set_header X-Forwarded-Proto $scheme;
74
+ }
75
+
76
+ location /integrations/ {
77
+ proxy_pass http://nest_integrations/integrations/;
78
+ proxy_http_version 1.1;
79
+ proxy_set_header Host $host;
80
+ proxy_set_header X-Real-IP $remote_addr;
81
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
82
+ proxy_set_header X-Forwarded-Proto $scheme;
83
+ # RDS / Cloud SQL provision waits up to ~15 min inside integrations
84
+ proxy_connect_timeout 1200s;
85
+ proxy_send_timeout 1200s;
86
+ proxy_read_timeout 1200s;
87
+ }
88
+
89
+ location /webhooks/ {
90
+ proxy_pass http://nest_webhooks/webhooks/;
91
+ proxy_http_version 1.1;
92
+ proxy_set_header Host $host;
93
+ proxy_set_header X-Real-IP $remote_addr;
94
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
95
+ proxy_set_header X-Forwarded-Proto $scheme;
96
+ }
97
+
98
+ location /notifications/ {
99
+ proxy_pass http://nest_notifications/notifications/;
100
+ proxy_http_version 1.1;
101
+ proxy_set_header Host $host;
102
+ proxy_set_header X-Real-IP $remote_addr;
103
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
104
+ proxy_set_header X-Forwarded-Proto $scheme;
105
+ }
106
+
107
+ location /partnerships/ {
108
+ proxy_pass http://nest_partnerships/partnerships/;
109
+ proxy_http_version 1.1;
110
+ proxy_set_header Host $host;
111
+ proxy_set_header X-Real-IP $remote_addr;
112
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
113
+ proxy_set_header X-Forwarded-Proto $scheme;
114
+ }
115
+
116
+ location /jobs/ {
117
+ proxy_pass http://nest_jobs/jobs/;
118
+ proxy_http_version 1.1;
119
+ proxy_set_header Host $host;
120
+ proxy_set_header X-Real-IP $remote_addr;
121
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
122
+ proxy_set_header X-Forwarded-Proto $scheme;
123
+ }
124
+
125
+ location /pricing/ {
126
+ proxy_pass http://nest_pricing/pricing/;
127
+ proxy_http_version 1.1;
128
+ proxy_set_header Host $host;
129
+ proxy_set_header X-Real-IP $remote_addr;
130
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
131
+ proxy_set_header X-Forwarded-Proto $scheme;
132
+ }
133
+
134
+ location /admins/ {
135
+ proxy_pass http://nest_admins/admins/;
136
+ proxy_http_version 1.1;
137
+ proxy_set_header Host $host;
138
+ proxy_set_header X-Real-IP $remote_addr;
139
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
140
+ proxy_set_header X-Forwarded-Proto $scheme;
141
+ }
142
+
143
+ location /proxy/ {
144
+ proxy_pass http://nest_proxy/proxy/;
145
+ proxy_http_version 1.1;
146
+ proxy_set_header Host $host;
147
+ proxy_set_header X-Real-IP $remote_addr;
148
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
149
+ proxy_set_header X-Forwarded-Proto $scheme;
150
+ proxy_connect_timeout 1200s;
151
+ proxy_send_timeout 1200s;
152
+ proxy_read_timeout 1200s;
153
+ }
154
+
155
+ location /tickets/ {
156
+ proxy_pass http://nest_tickets/tickets/;
157
+ proxy_http_version 1.1;
158
+ proxy_set_header Host $host;
159
+ proxy_set_header X-Real-IP $remote_addr;
160
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
161
+ proxy_set_header X-Forwarded-Proto $scheme;
162
+ }
163
+
164
+ location /realtime/ {
165
+ proxy_pass http://nest_realtime/;
166
+ proxy_http_version 1.1;
167
+ proxy_set_header Upgrade $http_upgrade;
168
+ proxy_set_header Connection "upgrade";
169
+ proxy_set_header Host $host;
170
+ proxy_set_header X-Real-IP $remote_addr;
171
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
172
+ proxy_set_header X-Forwarded-Proto $scheme;
173
+ }
174
+
175
+ location / {
176
+ return 404;
177
+ }
178
+ }
@@ -0,0 +1,265 @@
1
+ # Ductape platform — Docker Hub release compose
2
+ # Written by `ductape install` to ~/.ductape/platform/docker-compose.yml
3
+ # Run `ductape start` to bring the stack up.
4
+
5
+ name: ductape-platform
6
+
7
+ x-api-image: &api-image
8
+ image: ductape/platform-api:latest
9
+ restart: unless-stopped
10
+ depends_on:
11
+ mongo:
12
+ condition: service_healthy
13
+ kafka:
14
+ condition: service_started
15
+ networks:
16
+ - platform
17
+
18
+ x-api-env: &api-env
19
+ NODE_ENV: production
20
+ DB_HOST: mongodb://mongo:27017/ductape
21
+ KAFKA_BROKERS: kafka:29092
22
+ KAFKA_TOPIC_PREFIX: ductape
23
+ USE_KAFKA: "true"
24
+ KAFKA_RPC_TIMEOUT_MS: "15000"
25
+ EMAIL_SERVICE: http://emails:8003
26
+ USER_SERVICE: http://users:8002
27
+ WORKSPACE_SERVICE: http://workspaces:8001
28
+ PRICING_SERVICE: http://pricing:8011
29
+ NOTIFICATION_SERVICE: http://notifications:8013
30
+ ADMIN_SERVICE: http://admins:8018
31
+ APPS_SERVICE: http://apps:8004
32
+ DEFAULT_ADMIN_ROLE_ID: ${DEFAULT_ADMIN_ROLE_ID:-}
33
+ CORS_ORIGINS: http://localhost:4310,http://127.0.0.1:4310,http://localhost:4320,http://127.0.0.1:4320,http://localhost:3000,http://localhost
34
+ AWS_ACCOUNT_ID: ${AWS_ACCOUNT_ID:-480358138685}
35
+ AWS_DUCTAPE_ACCOUNT_ID: ${AWS_DUCTAPE_ACCOUNT_ID:-${AWS_ACCOUNT_ID:-480358138685}}
36
+
37
+ services:
38
+ mongo:
39
+ image: mongo:7
40
+ restart: unless-stopped
41
+ ports:
42
+ - "27017:27017"
43
+ volumes:
44
+ - mongo_data:/data/db
45
+ healthcheck:
46
+ test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
47
+ interval: 5s
48
+ timeout: 5s
49
+ retries: 10
50
+ networks:
51
+ - platform
52
+
53
+ zookeeper:
54
+ image: confluentinc/cp-zookeeper:7.6.1
55
+ environment:
56
+ ZOOKEEPER_CLIENT_PORT: 2181
57
+ ZOOKEEPER_TICK_TIME: 2000
58
+ ports:
59
+ - "2181:2181"
60
+ networks:
61
+ - platform
62
+
63
+ kafka:
64
+ image: confluentinc/cp-kafka:7.6.1
65
+ depends_on:
66
+ - zookeeper
67
+ ports:
68
+ - "9092:9092"
69
+ environment:
70
+ KAFKA_BROKER_ID: 1
71
+ KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
72
+ KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
73
+ KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092
74
+ KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
75
+ KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
76
+ KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
77
+ KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
78
+ KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"
79
+ networks:
80
+ - platform
81
+
82
+ api-gateway:
83
+ image: nginx:1.27-alpine
84
+ restart: unless-stopped
85
+ ports:
86
+ - "4311:8000"
87
+ volumes:
88
+ - ./api-gateway.conf:/etc/nginx/conf.d/default.conf:ro
89
+ depends_on:
90
+ - users
91
+ - workspaces
92
+ - apps
93
+ - proxy
94
+ networks:
95
+ - platform
96
+
97
+ users:
98
+ <<: *api-image
99
+ environment:
100
+ <<: *api-env
101
+ NEST_APP: users
102
+ PORT: "8002"
103
+ ports:
104
+ - "8002:8002"
105
+
106
+ workspaces:
107
+ <<: *api-image
108
+ environment:
109
+ <<: *api-env
110
+ NEST_APP: workspaces
111
+ PORT: "8001"
112
+ ports:
113
+ - "8001:8001"
114
+
115
+ emails:
116
+ <<: *api-image
117
+ environment:
118
+ <<: *api-env
119
+ NEST_APP: emails
120
+ PORT: "8003"
121
+ ports:
122
+ - "8003:8003"
123
+
124
+ apps:
125
+ <<: *api-image
126
+ environment:
127
+ <<: *api-env
128
+ NEST_APP: apps
129
+ PORT: "8004"
130
+ ports:
131
+ - "8004:8004"
132
+
133
+ integrations:
134
+ <<: *api-image
135
+ env_file:
136
+ - .env
137
+ environment:
138
+ <<: *api-env
139
+ NEST_APP: integrations
140
+ PORT: "8009"
141
+ AWS_REGION: ${AWS_REGION:-us-east-1}
142
+ AWS_SDK_LOAD_CONFIG: ${AWS_SDK_LOAD_CONFIG:-1}
143
+ AWS_PROFILE: ${AWS_PROFILE:-}
144
+ AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID:-}
145
+ AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY:-}
146
+ AWS_SESSION_TOKEN: ${AWS_SESSION_TOKEN:-}
147
+ volumes:
148
+ - ${HOME}/.aws:/root/.aws:ro
149
+ ports:
150
+ - "8009:8009"
151
+
152
+ pricing:
153
+ <<: *api-image
154
+ environment:
155
+ <<: *api-env
156
+ NEST_APP: pricing
157
+ PORT: "8011"
158
+ ports:
159
+ - "8011:8011"
160
+
161
+ logs:
162
+ <<: *api-image
163
+ environment:
164
+ <<: *api-env
165
+ NEST_APP: logs
166
+ PORT: "8008"
167
+ ports:
168
+ - "8008:8008"
169
+
170
+ proxy:
171
+ <<: *api-image
172
+ environment:
173
+ <<: *api-env
174
+ NEST_APP: proxy
175
+ PORT: "8020"
176
+ DUCTAPE_API_BASE_URL: http://api-gateway:8000
177
+ ports:
178
+ - "8020:8020"
179
+
180
+ admins:
181
+ <<: *api-image
182
+ environment:
183
+ <<: *api-env
184
+ NEST_APP: admins
185
+ PORT: "8018"
186
+ ports:
187
+ - "8018:8018"
188
+
189
+ notifications:
190
+ <<: *api-image
191
+ environment:
192
+ <<: *api-env
193
+ NEST_APP: notifications
194
+ PORT: "8013"
195
+ ports:
196
+ - "8013:8013"
197
+
198
+ webhooks:
199
+ <<: *api-image
200
+ environment:
201
+ <<: *api-env
202
+ NEST_APP: webhooks
203
+ PORT: "8012"
204
+ ports:
205
+ - "8012:8012"
206
+
207
+ jobs:
208
+ <<: *api-image
209
+ environment:
210
+ <<: *api-env
211
+ NEST_APP: jobs
212
+ PORT: "8015"
213
+ ports:
214
+ - "8015:8015"
215
+
216
+ marketplace:
217
+ <<: *api-image
218
+ environment:
219
+ <<: *api-env
220
+ NEST_APP: marketplace
221
+ PORT: "8007"
222
+ ports:
223
+ - "8007:8007"
224
+
225
+ partnerships:
226
+ <<: *api-image
227
+ environment:
228
+ <<: *api-env
229
+ NEST_APP: partnerships
230
+ PORT: "8014"
231
+ ports:
232
+ - "8014:8014"
233
+
234
+ tickets:
235
+ <<: *api-image
236
+ environment:
237
+ <<: *api-env
238
+ NEST_APP: tickets
239
+ PORT: "9002"
240
+ ports:
241
+ - "9002:9002"
242
+
243
+ realtime:
244
+ <<: *api-image
245
+ environment:
246
+ <<: *api-env
247
+ NEST_APP: realtime
248
+ PORT: "3030"
249
+ ports:
250
+ - "3030:3030"
251
+
252
+ workbench:
253
+ image: ductape/platform-workbench:latest
254
+ restart: unless-stopped
255
+ ports:
256
+ - "4310:80"
257
+ networks:
258
+ - platform
259
+
260
+ networks:
261
+ platform:
262
+ driver: bridge
263
+
264
+ volumes:
265
+ mongo_data:
@@ -0,0 +1,16 @@
1
+ # Ductape platform environment — edit and save as ~/.ductape/platform/.env
2
+ # Required for AWS cloud connections. All other vars have working defaults.
3
+
4
+ # --- AWS cloud connections ---
5
+ # Needed for workbench → Cloud → "Complete setup". Leave blank to skip cloud connections.
6
+ AWS_ACCOUNT_ID=480358138685
7
+ AWS_ACCESS_KEY_ID=
8
+ AWS_SECRET_ACCESS_KEY=
9
+ AWS_REGION=us-east-1
10
+ # AWS_SESSION_TOKEN=
11
+ # AWS_PROFILE=default
12
+ # AWS_SDK_LOAD_CONFIG=1
13
+
14
+ # --- Optional overrides ---
15
+ # DEFAULT_ADMIN_ROLE_ID=
16
+ # AWS_DUCTAPE_ACCOUNT_ID=480358138685