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.
- package/generators/devops.js +6 -1
- package/generators/kratos.js +60 -18
- package/generators/multitenancy.js +18 -9
- package/package.json +1 -1
package/generators/devops.js
CHANGED
|
@@ -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
|
|
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);
|
package/generators/kratos.js
CHANGED
|
@@ -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 := "
|
|
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
|
-
|
|
301
|
+
var mappings []models.TenantRole
|
|
300
302
|
if requestedTenant != "" {
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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
|
-
|
|
313
|
-
|
|
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
|
-
|
|
323
|
-
|
|
324
|
-
|
|
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
|
-
|
|
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
|
|
278
|
-
db.Raw("SELECT db_name, is_primary FROM tenant_roles WHERE tenant_id
|
|
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
|