go-duck-cli 1.1.27 → 1.1.30

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.
@@ -96,6 +96,7 @@ services:
96
96
  - "${dbPort}:5432"
97
97
  volumes:
98
98
  - postgres_data:/var/lib/postgresql/data
99
+ - ./postgres-init:/docker-entrypoint-initdb.d
99
100
  healthcheck:
100
101
  test: ["CMD-SHELL", "pg_isready -U ${config.datasource?.username || 'postgres'} -d ${config.datasource?.database || 'go_duck_master'}"]
101
102
  interval: 5s
@@ -167,7 +168,7 @@ services:
167
168
  KEYCLOAK_ADMIN: admin
168
169
  KEYCLOAK_ADMIN_PASSWORD: admin
169
170
  KC_DB: postgres
170
- KC_DB_URL: jdbc:postgresql://postgres:5432/${config.datasource?.database || 'go_duck_master'}
171
+ KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
171
172
  KC_DB_USERNAME: ${config.datasource?.username || 'postgres'}
172
173
  KC_DB_PASSWORD: ${config.datasource?.password || 'password'}
173
174
  depends_on:
@@ -392,6 +393,10 @@ jobs:
392
393
  await fs.ensureDir(realmConfigDir);
393
394
  await fs.writeFile(path.join(realmConfigDir, `${realmName}-realm.json`), realmJson);
394
395
 
396
+ const postgresInitDir = path.join(devopsDir, 'postgres-init');
397
+ await fs.ensureDir(postgresInitDir);
398
+ await fs.writeFile(path.join(postgresInitDir, 'init.sql'), 'CREATE DATABASE keycloak;\n');
399
+
395
400
  await fs.writeFile(path.join(devopsDir, 'Dockerfile'), dockerfile);
396
401
  await fs.writeFile(path.join(devopsDir, 'services.yml'), servicesYaml);
397
402
  await fs.writeFile(path.join(devopsDir, 'app.yml'), appYaml);
@@ -260,13 +260,15 @@ import (
260
260
  "{{projectName}}/models"
261
261
  my_middleware "{{projectName}}/middleware"
262
262
  "gorm.io/gorm"
263
+ "go.mongodb.org/mongo-driver/mongo"
263
264
  "strings"
264
265
  "fmt"
266
+ "sort"
265
267
  )
266
268
 
267
269
  func TenantServerInterceptor(conf *config.Config, db *gorm.DB) middleware.Middleware {
268
270
  mgr := my_middleware.GetTenantManager(db, conf)
269
- fallbackDB := "go_duck_fallback" // Standard fallback
271
+ fallbackDB := strings.ToLower(conf.GoDuck.Name) + "_fallback"
270
272
 
271
273
  return func(handler middleware.Handler) middleware.Handler {
272
274
  return func(ctx context.Context, req interface{}) (interface{}, error) {
@@ -296,36 +298,76 @@ func TenantServerInterceptor(conf *config.Config, db *gorm.DB) middleware.Middle
296
298
  requestedTenant = strings.ToLower(md.Get("x-tenant-id"))
297
299
  }
298
300
 
299
- siloConnections := make(map[string]*gorm.DB)
301
+ var mappings []models.TenantRole
300
302
  if requestedTenant != "" {
301
- var mapping models.TenantRole
302
- if err := db.Raw("SELECT role_name, db_name FROM tenant_roles WHERE role_name IN ? AND tenant_id = ? LIMIT 1", lowerRoles, requestedTenant).Scan(&mapping).Error; err == nil && mapping.DBName != "" {
303
- if mapping.DBName == "admin_db" && !isAdmin {
304
- conn, _ := mgr.GetDB(fallbackDB)
305
- siloConnections["fallback"] = conn
306
- } else {
307
- conn, _ := mgr.GetDB(mapping.DBName)
308
- siloConnections[mapping.RoleName] = conn
309
- }
303
+ db.Raw("SELECT role_name, db_name, is_primary FROM tenant_roles WHERE LOWER(role_name) IN ? AND tenant_id = ?", lowerRoles, requestedTenant).Scan(&mappings)
304
+ } else {
305
+ db.Raw("SELECT role_name, db_name, is_primary FROM tenant_roles WHERE LOWER(role_name) IN ?", lowerRoles).Scan(&mappings)
306
+ }
307
+
308
+ siloConnections := make(map[string]*gorm.DB)
309
+ mongoConnections := make(map[string]*mongo.Database)
310
+
311
+ if len(mappings) == 0 {
312
+ conn, _ := mgr.GetDB(fallbackDB)
313
+ siloConnections["fallback"] = conn
314
+ ctx = context.WithValue(ctx, "tenantDBConn", conn)
315
+
316
+ if conf.GoDuck.Datasource.MongoDB.Enabled {
317
+ mClient, err := mgr.GetMongoClient(fallbackDB)
318
+ if err == nil && mClient != nil {
319
+ mDB := mClient.Database(fallbackDB)
320
+ mongoConnections["fallback"] = mDB
321
+ ctx = context.WithValue(ctx, "tenantMongoDB", mDB)
322
+ }
310
323
  }
311
324
  } else {
312
- var mappings []models.TenantRole
313
- db.Raw("SELECT role_name, db_name FROM tenant_roles WHERE role_name IN ?", lowerRoles).Scan(&mappings)
325
+ isGenericRole := func(role string) bool {
326
+ r := strings.ToLower(role)
327
+ return r == "offline_access" || r == "uma_authorization" || strings.HasPrefix(r, "default-roles-")
328
+ }
329
+
330
+ // Sort mappings
331
+ sort.Slice(mappings, func(i, j int) bool {
332
+ if mappings[i].IsPrimary != mappings[j].IsPrimary {
333
+ return mappings[i].IsPrimary
334
+ }
335
+ gI := isGenericRole(mappings[i].RoleName)
336
+ gJ := isGenericRole(mappings[j].RoleName)
337
+ if gI != gJ {
338
+ return !gI
339
+ }
340
+ return false
341
+ })
342
+
314
343
  for _, m := range mappings {
315
344
  if m.DBName == "admin_db" && !isAdmin { continue }
316
345
  if conn, err := mgr.GetDB(m.DBName); err == nil {
317
346
  siloConnections[m.RoleName] = conn
318
347
  }
348
+ if conf.GoDuck.Datasource.MongoDB.Enabled {
349
+ mClient, err := mgr.GetMongoClient(m.DBName)
350
+ if err == nil && mClient != nil {
351
+ mongoConnections[m.RoleName] = mClient.Database(m.DBName)
352
+ }
353
+ }
319
354
  }
320
- }
321
355
 
322
- if len(siloConnections) == 0 {
323
- conn, _ := mgr.GetDB(fallbackDB)
324
- siloConnections["fallback"] = conn
356
+ // Lead/Primary Silo DB
357
+ primaryConn, err := mgr.GetDB(mappings[0].DBName)
358
+ if err == nil && primaryConn != nil {
359
+ ctx = context.WithValue(ctx, "tenantDBConn", primaryConn)
360
+ }
361
+ if conf.GoDuck.Datasource.MongoDB.Enabled {
362
+ mClient, err := mgr.GetMongoClient(mappings[0].DBName)
363
+ if err == nil && mClient != nil {
364
+ ctx = context.WithValue(ctx, "tenantMongoDB", mClient.Database(mappings[0].DBName))
365
+ }
366
+ }
325
367
  }
326
368
 
327
- // Inject Federated Bundle
328
369
  ctx = context.WithValue(ctx, "tenantSiloConns", siloConnections)
370
+ ctx = context.WithValue(ctx, "tenantMongoConnections", mongoConnections)
329
371
  return handler(ctx, req)
330
372
  }
331
373
  }
@@ -271,11 +271,21 @@ func PublicTenantMiddleware(db *gorm.DB, cfg *config.Config) gin.HandlerFunc {
271
271
  fallbackDB := strings.ToLower(cfg.GoDuck.Name) + "_fallback"
272
272
 
273
273
  return func(c *gin.Context) {
274
- requestedTenant := strings.ToLower(c.GetHeader("X-Tenant-ID"))
274
+ requestedTenantRaw := strings.ToLower(c.GetHeader("X-Tenant-ID"))
275
+ var requestedTenants []string
276
+ if requestedTenantRaw != "" {
277
+ parts := strings.Split(requestedTenantRaw, ",")
278
+ for _, p := range parts {
279
+ trimmed := strings.TrimSpace(p)
280
+ if trimmed != "" {
281
+ requestedTenants = append(requestedTenants, trimmed)
282
+ }
283
+ }
284
+ }
275
285
 
276
286
  var mappings []models.TenantRole
277
- if requestedTenant != "" {
278
- db.Raw("SELECT db_name, is_primary FROM tenant_roles WHERE tenant_id = ? ORDER BY is_primary DESC LIMIT 1", requestedTenant).Scan(&mappings)
287
+ if len(requestedTenants) > 0 {
288
+ db.Raw("SELECT db_name, is_primary FROM tenant_roles WHERE tenant_id IN ?", requestedTenants).Scan(&mappings)
279
289
  }
280
290
 
281
291
  dbName := fallbackDB
@@ -294,7 +304,7 @@ func PublicTenantMiddleware(db *gorm.DB, cfg *config.Config) gin.HandlerFunc {
294
304
 
295
305
  if cfg.GoDuck.Datasource.MongoDB.Enabled {
296
306
  mClient, err := mgr.GetMongoClient(dbName)
297
- if err == nil {
307
+ if err == nil && mClient != nil {
298
308
  c.Set("tenantMongoDB", mClient.Database(dbName))
299
309
  }
300
310
  }
@@ -336,7 +346,6 @@ func CreateDatabaseAndMigrate(masterDB *gorm.DB) gin.HandlerFunc {
336
346
  }
337
347
 
338
348
  req.DBName = strings.ToLower(req.DBName)
339
- req.RoleName = strings.ToLower(req.RoleName)
340
349
 
341
350
  // 1. Ensure DB exists
342
351
  var exists int
@@ -347,18 +356,18 @@ func CreateDatabaseAndMigrate(masterDB *gorm.DB) gin.HandlerFunc {
347
356
 
348
357
  // 2. Clear other primary flags for this role if setting new primary
349
358
  if req.IsPrimary {
350
- masterDB.Model(&models.TenantRole{}).Where("role_name = ?", req.RoleName).Update("is_primary", false)
359
+ masterDB.Model(&models.TenantRole{}).Where("LOWER(role_name) = LOWER(?)", req.RoleName).Update("is_primary", false)
351
360
  }
352
361
 
353
362
  // 3. Upsert mapping
354
363
  var existing models.TenantRole
355
364
  isPrimary := req.IsPrimary
356
365
  var count int64
357
- masterDB.Model(&models.TenantRole{}).Where("role_name = ?", req.RoleName).Count(&count)
366
+ masterDB.Model(&models.TenantRole{}).Where("LOWER(role_name) = LOWER(?)", req.RoleName).Count(&count)
358
367
  if count == 0 {
359
368
  isPrimary = true
360
369
  }
361
- if err := masterDB.Where("role_name = ? AND db_name = ?", req.RoleName, req.DBName).First(&existing).Error; err == nil {
370
+ if err := masterDB.Where("LOWER(role_name) = LOWER(?) AND db_name = ?", req.RoleName, req.DBName).First(&existing).Error; err == nil {
362
371
  if count <= 1 {
363
372
  isPrimary = true
364
373
  }
@@ -401,7 +410,7 @@ func GetMySilos(masterDB *gorm.DB) gin.HandlerFunc {
401
410
  }
402
411
 
403
412
  var mappings []models.TenantRole
404
- masterDB.Where("role_name IN ?", lowerRoles).Find(&mappings)
413
+ masterDB.Where("LOWER(role_name) IN ?", lowerRoles).Find(&mappings)
405
414
 
406
415
  appConfig, _ := config.LoadConfig()
407
416
  hideDBNames := appConfig.GoDuck.Multitenancy.HideSiloNames
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "go-duck-cli",
3
- "version": "1.1.27",
3
+ "version": "1.1.30",
4
4
  "description": "The Ultimate Evolutionary Go Microservice Scaffolder.",
5
5
  "main": "index.js",
6
6
  "type": "module",