package main import ( "fmt" "time" ) // Step — описание одного шага установки. Run выполняет шаг, может // проверить idempotency и вернуть Skipped. Логи прокидываются через // log-функцию, которая публикует event в SSE. type Step struct { ID string Title string Run func(s *State, log func(string)) (StepStatus, error) } // buildStepList — фиксированный порядок шагов установки. Соответствует // install-validata.sh + установка bj-server/crypto-service/ИШ. Меняется // атомарно (если что-то добавляется — добавляем сюда). func buildStepList() []StepState { steps := allSteps() out := make([]StepState, len(steps)) for i, s := range steps { out[i] = StepState{ID: s.ID, Title: s.Title, Status: StepPending} } return out } func allSteps() []Step { return []Step{ {ID: "deps", Title: "Установка системных зависимостей", Run: stepInstallDeps}, {ID: "validata-deb", Title: "Установка пакетов Валидаты (zpki + zsdk)", Run: stepInstallValidataDebs}, {ID: "execstack", Title: "execstack -c libvdcsp.so", Run: stepExecstack}, {ID: "bj-user", Title: "Создание пользователя bj и каталогов", Run: stepCreateBJUser}, {ID: "pcscd-dropin", Title: "Настройка pcscd (always-on)", Run: stepPcscdDropin}, {ID: "bj-crypto-dropins", Title: "Drop-ins для bj-crypto sandbox", Run: stepBJCryptoDropins}, {ID: "bj-server-dropin", Title: "Drop-in для bj-server", Run: stepBJServerDropin}, {ID: "spki-ini", Title: "Создание spki.ini", Run: stepSPKIIni}, {ID: "pki1-prep", Title: "Подготовка pki1.conf для bj", Run: stepPKI1Prep}, {ID: "usb-mount", Title: "Авто-mount USB через udev + systemd", Run: stepUSBMount}, {ID: "bj-server-binary", Title: "Установка bj-server бинаря в /opt/bj/", Run: stepInstallBJServer}, {ID: "crypto-jar", Title: "Установка crypto-service.jar", Run: stepInstallCryptoJar}, {ID: "systemd-units", Title: "systemd unit bj-crypto.service + bj-server.service", Run: stepSystemdUnits}, {ID: "ish-install", Title: "Установка ИШ НРД (если есть .deb)", Run: stepInstallISH}, {ID: "save-config", Title: "Сохранение setup.json", Run: stepSaveConfig}, {ID: "systemd-start", Title: "Запуск сервисов (pcscd, bj-crypto, bj-server)", Run: stepStartServices}, {ID: "health", Title: "Финальный health-check", Run: stepHealthCheck}, } } // runInstallation — основной цикл установки. Перебирает шаги, обновляет // статусы через State, прокидывает логи в SSE. Останавливается при первой // ошибке (UI покажет какой шаг + сообщение). func runInstallation(s *State) error { steps := allSteps() for _, step := range steps { now := time.Now() s.updateStep(step.ID, func(ss *StepState) { ss.Status = StepRunning ss.Started = &now ss.Message = "" }) logFn := func(line string) { s.updateStep(step.ID, func(ss *StepState) { ss.Message = line }) } status, err := step.Run(s, logFn) finished := time.Now() s.updateStep(step.ID, func(ss *StepState) { ss.Status = status ss.Finished = &finished if err != nil { ss.Message = err.Error() } }) if err != nil { return fmt.Errorf("шаг %q: %w", step.Title, err) } } return nil }