package bootstrap import ( "context" "errors" "fmt" "strings" "moku-backend/internal/jobs" "moku-backend/internal/posixproj" ) func (service *Service) enqueueBootstrapStructureMaterialization( ctx context.Context, installation *InstallationRecord, ) error { if installation == nil { return nil } jobStore := jobs.NewStore(service.db) if _, err := jobStore.Enqueue(ctx, jobs.EnqueueInput{ Kind: jobs.KindBootstrapStructureMaterialize, Payload: jobs.BootstrapStructureMaterializePayload{ InstallationID: installation.ID, }, }); err != nil { failure := fmt.Sprintf("enqueue bootstrap materialization job: %v", err) if markErr := service.markBootstrapMaterializationFailed(ctx, installation.ID, failure); markErr != nil { return errors.Join(err, markErr) } // The relational bootstrap write already committed successfully, so keep the // response successful and surface the enqueue problem via materialization state. installation.MaterializationStatus = materializationFailed installation.MaterializationError = &failure } return nil } func (service *Service) ProcessBootstrapStructureMaterialization(ctx context.Context, installationID string) error { trimmedInstallationID := strings.TrimSpace(installationID) if trimmedInstallationID == "" { return ErrInstallationNotConfigured } installation, err := service.GetInstallation(ctx) if err != nil { return err } if installation == nil || installation.ID != trimmedInstallationID { return ErrInstallationNotConfigured } if err := service.markBootstrapMaterializationRunning(ctx, trimmedInstallationID); err != nil { return err } if err := service.materializeBootstrapStructure(ctx, *installation); err != nil { failure := strings.TrimSpace(err.Error()) if failure == "" { failure = "bootstrap materialization failed" } if markErr := service.markBootstrapMaterializationFailed(ctx, trimmedInstallationID, failure); markErr != nil { return errors.Join(err, markErr) } return err } return service.markBootstrapMaterializationSucceeded(ctx, trimmedInstallationID) } func (service *Service) materializeBootstrapStructure(ctx context.Context, installation InstallationRecord) error { admin, err := service.GetAdmin(ctx) if err != nil { return err } if admin == nil { return ErrAdminNotConfigured } organization, err := service.loadPrimaryOrganization(ctx) if err != nil { return err } if organization == nil { return ErrBootstrapStructureMissing } department, err := service.loadPrimaryDepartment(ctx) if err != nil { return err } if department == nil { return ErrBootstrapStructureMissing } team, err := service.loadPrimaryTeam(ctx) if err != nil { return err } if team == nil { return ErrBootstrapStructureMissing } project, err := service.loadPrimaryProject(ctx) if err != nil { return err } if project == nil { return ErrBootstrapStructureMissing } if err := service.ensureBootstrapPOSIXSkeleton( installation, AdminSummary{ID: admin.ID, Email: admin.Email, DisplayName: admin.DisplayName}, namedRecord{ID: organization.ID, Name: organization.Name, Slug: organization.Slug}, namedRecord{ID: department.ID, Name: department.Name, Slug: department.Slug}, namedRecord{ID: team.ID, Name: team.Name, Slug: team.Slug}, namedRecord{ID: project.ID, Name: project.Name, Slug: project.Slug}, ); err != nil { return err } return service.rebuildProjection(ctx) } func (service *Service) rebuildProjection(ctx context.Context) error { if err := posixproj.NewProjector(service.db, service.posixRoot).Rebuild(ctx); err != nil { return fmt.Errorf("rebuild POSIX projection: %w", err) } return nil } func (service *Service) markBootstrapMaterializationRunning(ctx context.Context, installationID string) error { _, err := service.db.Pool.Exec(ctx, ` UPDATE installations SET materialization_status = 'running'::bootstrap_materialization_status, materialization_error = NULL, materialization_started_at = NOW(), materialization_finished_at = NULL, updated_at = NOW() WHERE id = $1::uuid; `, strings.TrimSpace(installationID)) return err } func (service *Service) markBootstrapMaterializationSucceeded(ctx context.Context, installationID string) error { _, err := service.db.Pool.Exec(ctx, ` UPDATE installations SET materialization_status = 'succeeded'::bootstrap_materialization_status, materialization_error = NULL, materialization_finished_at = NOW(), updated_at = NOW() WHERE id = $1::uuid; `, strings.TrimSpace(installationID)) return err } func (service *Service) markBootstrapMaterializationFailed(ctx context.Context, installationID, failure string) error { _, err := service.db.Pool.Exec(ctx, ` UPDATE installations SET materialization_status = 'failed'::bootstrap_materialization_status, materialization_error = $2, materialization_finished_at = NOW(), updated_at = NOW() WHERE id = $1::uuid; `, strings.TrimSpace(installationID), strings.TrimSpace(failure)) return err }