package bootstrap import ( "context" "fmt" "log" "time" "github.com/google/uuid" "github.com/ocdp/cluster-service/internal/adapter/output" "github.com/ocdp/cluster-service/internal/domain/entity" "github.com/ocdp/cluster-service/internal/pkg/password" ) // Seeder 预注入管理器 type Seeder struct { repos *output.Repositories passwordHasher *password.Hasher config *BootstrapConfig } // NewSeeder 创建预注入管理器 func NewSeeder(repos *output.Repositories, passwordHasher *password.Hasher, config *BootstrapConfig) *Seeder { return &Seeder{ repos: repos, passwordHasher: passwordHasher, config: config, } } // SeedAll 执行所有预注入 func (s *Seeder) SeedAll(ctx context.Context) error { if !s.config.Enabled { log.Println(" ℹ️ Bootstrap seeding is disabled") return nil } log.Println(" 🌱 Starting bootstrap seeding...") // 1. 注入用户 if err := s.seedUsers(ctx); err != nil { return fmt.Errorf("failed to seed users: %w", err) } // 2. 注入 Registries if err := s.seedRegistries(ctx); err != nil { return fmt.Errorf("failed to seed registries: %w", err) } // 3. 注入 Clusters if err := s.seedClusters(ctx); err != nil { return fmt.Errorf("failed to seed clusters: %w", err) } log.Println(" ✅ Bootstrap seeding completed") return nil } // seedUsers 注入用户 func (s *Seeder) seedUsers(ctx context.Context) error { if len(s.config.Users) == 0 { log.Println(" ↳ No users to seed") return nil } log.Printf(" ↳ Seeding %d user(s)...", len(s.config.Users)) for _, userSeed := range s.config.Users { // 检查用户是否已存在 existingUser, _ := s.repos.UserRepo.GetByUsername(ctx, userSeed.Username) if existingUser != nil { log.Printf(" ⊙ User '%s' already exists, skipping", userSeed.Username) continue } // 哈希密码 passwordHash, err := s.passwordHasher.Hash(userSeed.Password) if err != nil { log.Printf(" ✗ Failed to hash password for user '%s': %v", userSeed.Username, err) continue } // 创建用户 user := entity.NewUser(userSeed.Username, passwordHash, userSeed.Email) user.ID = uuid.New().String() if userSeed.Role != "" { user.Role = userSeed.Role } if user.Role == "admin" { user.WorkspaceID = entity.DefaultWorkspaceID } if err := s.repos.UserRepo.Create(ctx, user); err != nil { log.Printf(" ✗ Failed to create user '%s': %v", userSeed.Username, err) continue } log.Printf(" ✓ User '%s' created", userSeed.Username) } return nil } // seedRegistries 注入 Registries func (s *Seeder) seedRegistries(ctx context.Context) error { if len(s.config.Registries) == 0 { log.Println(" ↳ No registries to seed") return nil } log.Printf(" ↳ Seeding %d registry(ies)...", len(s.config.Registries)) ownerID := s.bootstrapOwnerID(ctx) for _, registrySeed := range s.config.Registries { // 检查 Registry 是否已存在 existingRegistry, _ := s.repos.RegistryRepo.GetByName(ctx, registrySeed.Name) if existingRegistry != nil { log.Printf(" ⊙ Registry '%s' already exists, skipping", registrySeed.Name) continue } // 创建 Registry registry := &entity.Registry{ ID: uuid.New().String(), Name: registrySeed.Name, WorkspaceID: entity.DefaultWorkspaceID, OwnerID: ownerID, Visibility: "global_shared", URL: registrySeed.URL, Description: registrySeed.Description, Username: registrySeed.Username, Password: registrySeed.Password, Insecure: registrySeed.Insecure, CreatedAt: time.Now(), UpdatedAt: time.Now(), } if err := s.repos.RegistryRepo.Create(ctx, registry); err != nil { log.Printf(" ✗ Failed to create registry '%s': %v", registrySeed.Name, err) continue } log.Printf(" ✓ Registry '%s' created (credentials encrypted)", registrySeed.Name) } return nil } // seedClusters 注入 Clusters func (s *Seeder) seedClusters(ctx context.Context) error { if len(s.config.Clusters) == 0 { log.Println(" ↳ No clusters to seed") return nil } log.Printf(" ↳ Seeding %d cluster(s)...", len(s.config.Clusters)) ownerID := s.bootstrapOwnerID(ctx) for _, clusterSeed := range s.config.Clusters { // 检查 Cluster 是否已存在 existingCluster, _ := s.repos.ClusterRepo.GetByName(ctx, clusterSeed.Name) if existingCluster != nil { log.Printf(" ⊙ Cluster '%s' already exists, skipping", clusterSeed.Name) continue } // 创建 Cluster cluster := &entity.Cluster{ ID: uuid.New().String(), Name: clusterSeed.Name, WorkspaceID: entity.DefaultWorkspaceID, OwnerID: ownerID, Visibility: "global_shared", Host: clusterSeed.Host, Description: clusterSeed.Description, CAData: clusterSeed.CAData, CertData: clusterSeed.CertData, KeyData: clusterSeed.KeyData, Token: clusterSeed.Token, CreatedAt: time.Now(), UpdatedAt: time.Now(), } if err := s.repos.ClusterRepo.Create(ctx, cluster); err != nil { log.Printf(" ✗ Failed to create cluster '%s': %v", clusterSeed.Name, err) continue } log.Printf(" ✓ Cluster '%s' created (credentials encrypted)", clusterSeed.Name) } return nil } func (s *Seeder) bootstrapOwnerID(ctx context.Context) string { for _, userSeed := range s.config.Users { if userSeed.Role == "admin" { if user, err := s.repos.UserRepo.GetByUsername(ctx, userSeed.Username); err == nil && user != nil { return user.ID } } } users, err := s.repos.UserRepo.List(ctx) if err != nil { return "" } for _, user := range users { if user.Role == "admin" { return user.ID } } return "" }