package bootstrap import ( "baron-sso-backend/internal/domain" "errors" "fmt" "log/slog" "os" "golang.org/x/crypto/bcrypt" "gorm.io/gorm" ) // Run executes the application bootstrap logic (migrations, seeding, etc.) func Run(db *gorm.DB) error { slog.Info("[Bootstrap] Starting application bootstrap...") // 1. Auto Migration if err := migrateSchemas(db); err != nil { return fmt.Errorf("migration failed: %w", err) } // 2. Seed Initial Admin User if err := seedAdminUser(db); err != nil { return fmt.Errorf("seeding admin failed: %w", err) } slog.Info("[Bootstrap] Bootstrap completed successfully.") return nil } func migrateSchemas(db *gorm.DB) error { slog.Info("[Bootstrap] Migrating database schemas...") // Add all domain models here return db.AutoMigrate( &domain.User{}, &domain.Tenant{}, // &domain.RelyingParty{}, // TODO: Uncomment when model is ready // &domain.UserConsent{}, // TODO: Uncomment when model is ready ) } func seedAdminUser(db *gorm.DB) error { adminEmail := os.Getenv("ADMIN_EMAIL") adminPassword := os.Getenv("ADMIN_PASSWORD") if adminEmail == "" || adminPassword == "" { slog.Warn("[Bootstrap] ADMIN_EMAIL or ADMIN_PASSWORD not set. Skipping admin seeding.") return nil } var user domain.User if err := db.Unscoped().Where("email = ?", adminEmail).First(&user).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { slog.Info("[Bootstrap] Creating initial admin user", "email", adminEmail) hashedPassword, err := bcrypt.GenerateFromPassword([]byte(adminPassword), bcrypt.DefaultCost) if err != nil { return err } adminUser := domain.User{ Email: adminEmail, PasswordHash: string(hashedPassword), Name: "System Admin", Role: "admin", // Assuming 'role' field exists or handling via attributes // Add other required fields } if err := db.Create(&adminUser).Error; err != nil { return err } slog.Info("[Bootstrap] Admin user created successfully.") } else { return err } } else { slog.Info("[Bootstrap] Admin user already exists.", "email", adminEmail) } return nil }