go-duck-cli 1.3.2 → 1.3.5

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.
@@ -6,6 +6,9 @@ export const generateSwaggerDocs = async (config, entities, outputDir, openEntit
6
6
  const docsDir = path.join(outputDir, 'docs');
7
7
  await fs.ensureDir(docsDir);
8
8
 
9
+ const apiPrefixRaw = config.server?.rest?.['api-path-prefix'] || '/api';
10
+ const apiPrefix = apiPrefixRaw.endsWith('/') ? apiPrefixRaw.slice(0, -1) : apiPrefixRaw;
11
+
9
12
  const swagger = {
10
13
  openapi: '3.0.0',
11
14
  info: {
@@ -14,7 +17,7 @@ export const generateSwaggerDocs = async (config, entities, outputDir, openEntit
14
17
  description: `Generated documentation for ${config.name} microservice`
15
18
  },
16
19
  servers: [
17
- { url: `http://localhost:${config.server?.port || 8080}`, description: 'Local Development Server' }
20
+ { url: "/", description: "Current Host" }
18
21
  ],
19
22
  paths: {},
20
23
  components: {
@@ -48,7 +51,7 @@ export const generateSwaggerDocs = async (config, entities, outputDir, openEntit
48
51
  };
49
52
 
50
53
  const commonHeaders = [
51
- { name: 'X-Tenant-ID', in: 'header', required: true, schema: { type: 'string', default: 'default' }, description: 'Multi-tenancy context identifier' }
54
+ { name: 'X-Tenant-ID', in: 'header', required: false, schema: { type: 'string', default: 'default' }, description: 'Multi-tenancy context identifier' }
52
55
  ];
53
56
 
54
57
  const isOpen = (entityName, action) => {
@@ -81,7 +84,7 @@ export const generateSwaggerDocs = async (config, entities, outputDir, openEntit
81
84
  properties: {
82
85
  id: { type: 'integer' },
83
86
  ...entity.fields.reduce((acc, field) => {
84
- acc[field.name] = { type: mapToSwaggerType(field.type) };
87
+ acc[field.name] = getSwaggerFieldSchema(field.type);
85
88
  return acc;
86
89
  }, {}),
87
90
  createdAt: { type: 'string', format: 'date-time' },
@@ -201,14 +204,14 @@ export const generateSwaggerDocs = async (config, entities, outputDir, openEntit
201
204
  };
202
205
 
203
206
  // 1a. Secured Paths
204
- addEntityOperations('/api', false);
207
+ addEntityOperations(apiPrefix, false);
205
208
 
206
209
  // 1b. Public Paths (if marked as open)
207
- addEntityOperations('/open/api', true);
210
+ addEntityOperations('/open' + apiPrefix, true);
208
211
  }
209
212
 
210
213
  // 2. Add System Paths
211
- swagger.paths['/api/rpc/{table}'] = {
214
+ swagger.paths[`${apiPrefix}/rpc/{table}`] = {
212
215
  get: {
213
216
  tags: ['Search Engine'],
214
217
  summary: 'Generic PostgREST RPC Engine',
@@ -240,7 +243,7 @@ export const generateSwaggerDocs = async (config, entities, outputDir, openEntit
240
243
  }
241
244
  };
242
245
 
243
- swagger.paths['/api/admin/audit'] = {
246
+ swagger.paths[`${apiPrefix}/admin/audit`] = {
244
247
  get: {
245
248
  tags: ['Observability'],
246
249
  summary: 'Fetch Audit Trail',
@@ -270,7 +273,7 @@ export const generateSwaggerDocs = async (config, entities, outputDir, openEntit
270
273
  }
271
274
  },
272
275
  parameters: [
273
- { name: 'X-Tenant-ID', in: 'header', required: true, schema: { type: 'string', default: 'master_internal' }, description: 'SuperAdmin internal master bypass token' }
276
+ { name: 'X-Tenant-ID', in: 'header', required: false, schema: { type: 'string', default: 'master_internal' }, description: 'SuperAdmin internal master bypass token' }
274
277
  ],
275
278
  responses: {
276
279
  200: { description: 'Success' },
@@ -281,7 +284,7 @@ export const generateSwaggerDocs = async (config, entities, outputDir, openEntit
281
284
  };
282
285
 
283
286
  if (config.elasticsearch?.enabled) {
284
- swagger.paths['/api/search/{entity}'] = {
287
+ swagger.paths[`${apiPrefix}/search/{entity}`] = {
285
288
  get: {
286
289
  tags: ['Search Engine'],
287
290
  summary: 'Elasticsearch Global Search',
@@ -315,19 +318,19 @@ export const generateSwaggerDocs = async (config, entities, outputDir, openEntit
315
318
  console.log(chalk.gray(' Generated Swagger Documentation: swagger.json'));
316
319
  };
317
320
 
318
- const mapToSwaggerType = (type) => {
319
- const types = {
320
- 'String': 'string',
321
- 'Integer': 'integer',
322
- 'Float': 'number',
323
- 'Boolean': 'boolean',
324
- 'Long': 'integer',
325
- 'BigDecimal': 'number',
326
- 'LocalDate': 'string',
327
- 'Instant': 'string',
328
- 'JSON': 'object',
329
- 'JSONB': 'object',
330
- 'Text': 'string'
321
+ const getSwaggerFieldSchema = (type) => {
322
+ const schemas = {
323
+ 'String': { type: 'string' },
324
+ 'Integer': { type: 'integer', format: 'int32' },
325
+ 'Float': { type: 'number', format: 'double' },
326
+ 'Boolean': { type: 'boolean' },
327
+ 'Long': { type: 'integer', format: 'int64' },
328
+ 'BigDecimal': { type: 'number', format: 'double' },
329
+ 'LocalDate': { type: 'string', format: 'date' },
330
+ 'Instant': { type: 'string', format: 'date-time' },
331
+ 'JSON': { type: 'object' },
332
+ 'JSONB': { type: 'object' },
333
+ 'Text': { type: 'string' }
331
334
  };
332
- return types[type] || 'string';
335
+ return schemas[type] || { type: 'string' };
333
336
  };
package/index.js CHANGED
@@ -31,6 +31,7 @@ import { generateWebSocketCode } from './generators/websocket.js';
31
31
  import { generateConfigLoader } from './generators/config.js';
32
32
  import { generateLoggerCode } from './generators/logger.js';
33
33
  import { generateMQTTCode } from './generators/mqtt.js';
34
+ import { generateMQTTTopicsJSON } from './generators/mqtt-topics.js';
34
35
  import { generateCacheCode } from './generators/cache.js';
35
36
  import { generateResilienceCode } from './generators/resilience.js';
36
37
  import { generateTelemetryCode } from './generators/telemetry.js';
@@ -875,13 +876,21 @@ const generateYAMLConfigs = async (config, outputDir) => {
875
876
  delete cleanConfig.datasource['multitenancy-databases'];
876
877
  }
877
878
 
879
+ const toKebabCase = (str) => {
880
+ if (!str) return '';
881
+ return str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
882
+ };
883
+
884
+ const rawApiPrefix = cleanConfig.server?.rest?.['api-path-prefix'] || `/${cleanConfig.name || 'api'}/api`;
885
+ const kebabApiPrefix = rawApiPrefix.split('/').map(segment => toKebabCase(segment)).join('/');
886
+
878
887
  const extendedConfig = {
879
888
  ...cleanConfig,
880
889
  server: {
881
890
  rest: {
882
891
  port: cleanConfig.server?.rest?.port || cleanConfig.server?.port || 8080,
883
892
  protocol: cleanConfig.server?.rest?.protocol || 'json',
884
- 'api-path-prefix': cleanConfig.server?.rest?.['api-path-prefix'] || `/${cleanConfig.name || 'api'}/api`
893
+ 'api-path-prefix': kebabApiPrefix
885
894
  },
886
895
  'read-timeout': cleanConfig.server?.['read-timeout'] || '30s',
887
896
  'write-timeout': cleanConfig.server?.['write-timeout'] || '30s',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "go-duck-cli",
3
- "version": "1.3.2",
3
+ "version": "1.3.5",
4
4
  "description": "The Ultimate Evolutionary Go Microservice Scaffolder.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -10,6 +10,9 @@
10
10
  "scripts": {
11
11
  "test": "echo \"Error: no test specified\" && exit 1"
12
12
  },
13
+ "engines": {
14
+ "node": ">=18.0.0"
15
+ },
13
16
  "author": "heavenscode",
14
17
  "license": "ISC",
15
18
  "dependencies": {
@@ -21,5 +24,23 @@
21
24
  "inquirer": "^8.2.7",
22
25
  "js-yaml": "^4.1.1",
23
26
  "open": "^11.0.0"
27
+ },
28
+ "keywords": [
29
+ "go",
30
+ "golang",
31
+ "microservice",
32
+ "scaffold",
33
+ "cli",
34
+ "generator",
35
+ "go-duck"
36
+ ],
37
+
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "git+https://github.com/heavenscode/go-duck.git"
41
+ },
42
+ "homepage": "https://goduck.theheavenscode.com",
43
+ "bugs": {
44
+ "url": "https://github.com/heavenscode/go-duck/issues"
24
45
  }
25
46
  }
@@ -16,7 +16,7 @@
16
16
  <span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75"></span>
17
17
  <span class="relative inline-flex rounded-full h-3 w-3 bg-emerald-500"></span>
18
18
  </span>
19
- <p class="text-[11px] font-black text-indigo-900 uppercase tracking-[0.25em]">The 395% Elite Milestone Surpassed</p>
19
+ <p class="text-[11px] font-black text-indigo-900 uppercase tracking-[0.25em]">The 420% Elite Milestone Surpassed</p>
20
20
  </div>
21
21
 
22
22
  <!-- Heroic Logo Anchor (Enhanced) -->
@@ -121,6 +121,16 @@
121
121
  </div>
122
122
  </div>
123
123
 
124
+ <div class="lg:col-span-12 bg-white p-10 rounded-[2.5rem] border border-amber-100 shadow-sm hover:shadow-2xl transition-all group relative overflow-hidden cursor-crosshair bg-gradient-to-br from-white to-amber-50/30">
125
+ <p class="text-[9px] font-bold text-amber-600 uppercase tracking-widest mb-4">Elite Extension (+10%)</p>
126
+ <h4 class="text-2xl font-black text-slate-900 mb-3 tracking-tight italic">API Gateway Standards & Swagger UI</h4>
127
+ <p class="text-slate-600 leading-relaxed mb-8">Natively exposes OpenAPI JSON at the JHipster-standard <code>/v3/api-docs</code> endpoint. Features a completely re-engineered, glassmorphism Swagger UI deeply integrated with Keycloak SSO and automatic token refreshing.</p>
128
+ <div class="flex gap-3">
129
+ <span class="px-4 py-1.5 bg-amber-100 text-amber-700 text-[9px] font-black rounded-lg">JHIPSTER COMPATIBLE</span>
130
+ <span class="px-4 py-1.5 bg-amber-100 text-amber-700 text-[9px] font-black rounded-lg">KEYCLOAK SSO UI</span>
131
+ </div>
132
+ </div>
133
+
124
134
  <div class="lg:col-span-12 bg-white p-10 rounded-[2.5rem] border border-fuchsia-100 shadow-sm hover:shadow-2xl transition-all group relative overflow-hidden cursor-crosshair bg-gradient-to-br from-white to-fuchsia-50/30">
125
135
  <p class="text-[9px] font-bold text-fuchsia-600 uppercase tracking-widest mb-4">Elite Extension (+12%)</p>
126
136
  <h4 class="text-2xl font-black text-slate-900 mb-3 tracking-tight italic">K8s Network Isolation & NodePorts</h4>
@@ -436,7 +446,7 @@
436
446
  </div>
437
447
 
438
448
  <div class="mt-24 text-slate-500 font-mono text-[10px] uppercase tracking-[0.6em] font-black relative z-10">
439
- GO-DUCK &bull; THE 395% ELITE MILESTONE &bull; SOVEREIGN CODE ORCHESTRATION
449
+ GO-DUCK &bull; THE 420% ELITE MILESTONE &bull; SOVEREIGN CODE ORCHESTRATION
440
450
  </div>
441
451
  </div>
442
452
  </section>
@@ -86,8 +86,13 @@ class ApiProvider {
86
86
  <h2 class="text-2xl font-bold text-gray-800 mb-4 border-b pb-2">3. WSO2 API Manager Integration</h2>
87
87
  <p class="mb-4">GO-DUCK generated APIs can automatically register themselves with a <strong>WSO2 API Manager</strong> instance on startup. This uses the WSO2 Publisher REST API to import your dynamically generated OpenAPI 3.0 specs.</p>
88
88
 
89
+ <div class="bg-indigo-50 border-l-4 border-indigo-500 p-4 mb-6 rounded-r">
90
+ <h4 class="font-bold text-indigo-900 mb-1">Standard API Gateway Discovery</h4>
91
+ <p class="text-sm text-indigo-800">For external gateways (Kong, Apigee) and ecosystems like <strong>JHipster</strong> and <strong>Spring Boot</strong>, your microservice natively exposes its OpenAPI JSON at the standard <code>/v3/api-docs</code> endpoint. Legacy systems can still use <code>/swagger.json</code>.</p>
92
+ </div>
93
+
89
94
  <div class="bg-blue-50 border-l-4 border-blue-500 p-4 mb-6 rounded-r">
90
- <p class="text-blue-900"><strong>Configuration:</strong> Enable this by adding the <code>wso2</code> block to your <code>config.yaml</code>.</p>
95
+ <p class="text-blue-900"><strong>WSO2 Configuration:</strong> Enable this by adding the <code>wso2</code> block to your <code>config.yaml</code>.</p>
91
96
  </div>
92
97
 
93
98
  <pre><code class="language-yaml">go-duck:
@@ -29,6 +29,12 @@ messaging.Publish("events/users", event)</code></pre>
29
29
  mosquitto_sub -h localhost -p {{mqttPort}} -t "audit/logs/#" -v</code></pre>
30
30
  </section>
31
31
 
32
+ <section class="mb-10">
33
+ <h2 class="text-2xl font-bold text-gray-800 mb-4 border-b pb-2">Interactive Swagger Console</h2>
34
+ <p class="mb-4">GO-DUCK's automatically generated Swagger UI features a built-in <strong>Interactive MQTT Topics Dictionary</strong>. By connecting via WebSockets, it allows authenticated users to dynamically <strong>SUBSCRIBE</strong> to event streams or <strong>PUBLISH</strong> payloads directly from the documentation interface!</p>
35
+ <p class="mb-4 text-sm text-gray-600 italic">Note: Ensure your Mosquitto broker is configured to accept WebSocket connections (default port 9001) for this feature to work.</p>
36
+ </section>
37
+
32
38
  <section class="mb-10">
33
39
  <h2 class="text-2xl font-bold text-gray-800 mb-4 border-b pb-2">External Resources</h2>
34
40
  <ul class="space-y-2">