package bootstrap import ( "context" "fmt" "haixun-backend/internal/config" libcrypto "haixun-backend/internal/library/crypto" libmongo "haixun-backend/internal/library/mongo" brandrepo "haixun-backend/internal/model/brand/repository" cmatrixrepo "haixun-backend/internal/model/content_matrix/repository" copydraftrepo "haixun-backend/internal/model/copy_draft/repository" copymissionrepo "haixun-backend/internal/model/copy_mission/repository" jobrepo "haixun-backend/internal/model/job/repository" kgrepo "haixun-backend/internal/model/knowledge_graph/repository" memberrepo "haixun-backend/internal/model/member/repository" outreachdraftrepo "haixun-backend/internal/model/outreach_draft/repository" permissionrepo "haixun-backend/internal/model/permission/repository" permissionuc "haixun-backend/internal/model/permission/usecase" personarepo "haixun-backend/internal/model/persona/repository" placementtopicrepo "haixun-backend/internal/model/placement_topic/repository" scanpostrepo "haixun-backend/internal/model/scan_post/repository" settingrepo "haixun-backend/internal/model/setting/repository" threadsaccountrepo "haixun-backend/internal/model/threads_account/repository" ) type InitOptions struct { TenantID string AdminEmail string AdminPass string DisplayName string } type InitReport struct { IndexesEnsured bool PermissionsSeeded bool RolePermissionsSeeded bool AdminUID string AdminCreated bool } func Init(ctx context.Context, cfg config.Config, opts InitOptions) (*InitReport, error) { if cfg.Mongo.URI == "" || cfg.Mongo.Database == "" { return nil, fmt.Errorf("mongo URI and database are required") } if opts.TenantID == "" { return nil, fmt.Errorf("tenant_id is required") } if opts.AdminEmail == "" || opts.AdminPass == "" { return nil, fmt.Errorf("admin email and password are required") } mongoClient, err := libmongo.NewClient(ctx, cfg.Mongo) if err != nil { return nil, fmt.Errorf("connect mongo: %w", err) } defer func() { _ = mongoClient.Close(ctx) }() db := mongoClient.Database() report := &InitReport{} // cipher 只用於資料加解密;EnsureIndexes 不會用到,但 secrets repo 建構子需要它。 secretsCipher, err := libcrypto.New(cfg.Secrets.EncryptionKey) if err != nil { return nil, fmt.Errorf("init secrets cipher: %w", err) } settingRepository := settingrepo.NewMongoRepository(db) memberRepository := memberrepo.NewMongoRepository(db) permissionRepository := permissionrepo.NewMongoPermissionRepository(db) rolePermissionRepository := permissionrepo.NewMongoRolePermissionRepository(db) jobTemplateRepository := jobrepo.NewMongoTemplateRepository(db) jobRunRepository := jobrepo.NewMongoRunRepository(db) jobScheduleRepository := jobrepo.NewMongoScheduleRepository(db) jobEventRepository := jobrepo.NewMongoEventRepository(db) repos := []struct { name string fn func(context.Context) error }{ {"settings", settingRepository.EnsureIndexes}, {"members", memberRepository.EnsureIndexes}, {"permissions", permissionRepository.EnsureIndexes}, {"role_permissions", rolePermissionRepository.EnsureIndexes}, {"job_templates", jobTemplateRepository.EnsureIndexes}, {"job_runs", jobRunRepository.EnsureIndexes}, {"job_schedules", jobScheduleRepository.EnsureIndexes}, {"job_events", jobEventRepository.EnsureIndexes}, {"copy_missions", copymissionrepo.NewMongoRepository(db).EnsureIndexes}, {"copy_drafts", copydraftrepo.NewMongoRepository(db).EnsureIndexes}, {"scan_posts", scanpostrepo.NewMongoRepository(db).EnsureIndexes}, {"outreach_drafts", outreachdraftrepo.NewMongoRepository(db).EnsureIndexes}, {"content_matrix", cmatrixrepo.NewMongoRepository(db).EnsureIndexes}, {"knowledge_graph", kgrepo.NewMongoRepository(db).EnsureIndexes}, {"personas", personarepo.NewMongoRepository(db).EnsureIndexes}, {"brands", brandrepo.NewMongoRepository(db).EnsureIndexes}, {"placement_topics", placementtopicrepo.NewMongoRepository(db).EnsureIndexes}, {"threads_accounts", threadsaccountrepo.NewMongoRepository(db).EnsureIndexes}, {"threads_account_secrets", threadsaccountrepo.NewSecretsMongoRepository(db, secretsCipher).EnsureIndexes}, } for _, repo := range repos { if err := repo.fn(ctx); err != nil { return nil, fmt.Errorf("ensure %s indexes: %w", repo.name, err) } } report.IndexesEnsured = true permissionUseCase := permissionuc.NewUseCase(permissionRepository, rolePermissionRepository) if err := permissionUseCase.EnsureDefaultPermissions(ctx); err != nil { return nil, fmt.Errorf("seed permissions catalog: %w", err) } report.PermissionsSeeded = true if err := permissionUseCase.EnsureDefaultRolePermissions(ctx, opts.TenantID); err != nil { return nil, fmt.Errorf("seed role permissions: %w", err) } report.RolePermissionsSeeded = true admin, created, err := EnsureAdminMember(ctx, memberRepository, AdminOptions{ TenantID: opts.TenantID, Email: opts.AdminEmail, Password: opts.AdminPass, DisplayName: opts.DisplayName, }) if err != nil { return nil, err } report.AdminUID = admin.UID report.AdminCreated = created return report, nil }