diff --git a/internal/lkgateway/keywizard.go b/internal/lkgateway/keywizard.go index ad8672c..543c3ba 100644 --- a/internal/lkgateway/keywizard.go +++ b/internal/lkgateway/keywizard.go @@ -351,6 +351,7 @@ func (h *setupHandlers) keyWizardUseMedia(w http.ResponseWriter, r *http.Request h.keyWiz.set(3, "ok", "ИШ перезапущен и работает: "+ishMsg) h.keyWiz.set(4, "ok", "Ключ с флешки подключён — проверьте подключения на странице «Статус»") h.keyWiz.Done = true + keyWizInstalled.Store(true) h.keyWiz.mu.Unlock() _ = os.RemoveAll(staging) refreshCertStore(h.rc) @@ -593,6 +594,7 @@ func (h *setupHandlers) keyWizardInstall(w http.ResponseWriter, r *http.Request) h.keyWiz.set(4, "ok", "Ключ установлен — проверьте подключения на странице «Статус»") } h.keyWiz.Done = true + keyWizInstalled.Store(true) h.keyWiz.mu.Unlock() // Ключ уже на флешке и в ИШ — распаковка staging больше не нужна. diff --git a/internal/lkgateway/onboarding_test.go b/internal/lkgateway/onboarding_test.go index 912298e..bf37ca6 100644 --- a/internal/lkgateway/onboarding_test.go +++ b/internal/lkgateway/onboarding_test.go @@ -210,16 +210,20 @@ func TestWizardChaptersWithoutLicense(t *testing.T) { rc, _ := NewRuntimeConfig(t.TempDir() + "/setup.json") done := map[int]bool{2: true, 6: true} path, chapters := wizPath(rc, done, 4) - want := []int{14, 1, 12, 4, 7, 8, 9, 13, 10} // обновление — первый пункт; без лицензии нет учёта DEPO, QUIK и модулей; база и Валидата — сделаны; учётные записи — перед проверкой + want := []int{1, 14, 12, 4, 7, 8, 9, 13, 10} // обновление — сразу после лицензии; без лицензии нет учёта DEPO, QUIK и модулей; база и Валидата — сделаны; учётные записи — перед проверкой if fmt.Sprint(path) != fmt.Sprint(want) { t.Fatalf("путь %v, ожидался %v", path, want) } - if len(chapters) != 5 || !chapters[2].Current || chapters[2].Steps[0].N != 4 { + if len(chapters) != 4 || !chapters[1].Current || chapters[1].Steps[0].N != 4 { t.Fatalf("главы: %+v", chapters) } if p, n := wizNeighbours(path, 4); p != 12 || n != 7 { t.Fatalf("соседи шага 4: %d %d", p, n) } + // шаг «База bj» сделан установщиком и в пути его нет (открыт по ссылке) — соседи по порядку глав, не начало + if p, n := wizNeighbours(path, 2); p != 14 || n != 12 { + t.Fatalf("соседи шага 2 вне пути: %d %d", p, n) + } if d := wizDefaultStep(path, map[int]bool{1: true, 4: false}); d != 4 { t.Fatalf("по умолчанию: %d", d) } @@ -233,10 +237,10 @@ func TestWizardChaptersWithoutLicense(t *testing.T) { if len(wizModuleViews(rc, rc.Snapshot())) != 0 { t.Fatal("без лицензии модулей в мастере быть не должно") } - if chapters[2].Title != "Организация" { - t.Fatalf("без учёта DEPO и QUIK глава «Организация», а не %q", chapters[2].Title) + if chapters[1].Title != "Организация" { + t.Fatalf("без учёта DEPO и QUIK глава «Организация», а не %q", chapters[1].Title) } - if l := wizChaptersLine(chapters); l != "Пять глав: обновление, сервер, организация, НРД, запуск." { + if l := wizChaptersLine(chapters); l != "Четыре главы: сервер, организация, НРД, запуск." { t.Fatalf("подзаголовок: %q", l) } } diff --git a/internal/lkgateway/setup.go b/internal/lkgateway/setup.go index 7f61246..31a32c9 100644 --- a/internal/lkgateway/setup.go +++ b/internal/lkgateway/setup.go @@ -198,6 +198,8 @@ type WizardData struct { Chapters []WizChapter Prev, Next int StepDone bool + Unfinished int // обязательный несделанный шаг (последний шаг ведёт к нему, а не «к работе») + UnfinishedT string // его название StepRequired bool StepTitle string ChapterTitle string @@ -293,7 +295,7 @@ func (h *setupHandlers) renderWizard(w http.ResponseWriter, r *http.Request) { // установщиком. Без ?step — первый несделанный обязательный шаг, а если всё сделано — проверка. d.Done.Users = doneUsers() // Выполнено ли — одно правило с плашкой на главной (wizDone), с учётом «Настроить позже». - done := wizDone(r.Context(), h.rc, d.ValidataVer, d.Done.Certs || h.keyWiz.installed()) + done := wizDone(r.Context(), h.rc, d.ValidataVer, d.Done.Certs || h.keyWiz.installed() || keyWizInstalled.Load()) // «отложено» показываем, только пока шаг не настроен по-настоящему d.Deferred = map[int]bool{9: def.Steps[9] && !d.Done.Alerts, 11: def.Steps[11] && !d.Done.Modules, 13: def.Steps[13] && !d.Done.Users} path, _ := wizPath(h.rc, done, 0) @@ -307,6 +309,9 @@ func (h *setupHandlers) renderWizard(w http.ResponseWriter, r *http.Request) { d.Path, d.Chapters = wizPath(h.rc, done, step) d.Prev, d.Next = wizNeighbours(d.Path, step) d.StepDone, d.StepRequired, d.StepTitle = done[step], wizRequired[step], wizStepTitles[step] + if u := wizDefaultStep(path, done); wizRequired[u] && !done[u] && u != step { + d.Unfinished, d.UnfinishedT = u, wizStepTitles[u] + } for _, c := range d.Chapters { if c.Current { d.ChapterTitle = c.Title diff --git a/internal/lkgateway/setupsteps.go b/internal/lkgateway/setupsteps.go index a65c3dc..1f7bb90 100644 --- a/internal/lkgateway/setupsteps.go +++ b/internal/lkgateway/setupsteps.go @@ -6,6 +6,7 @@ package lkgateway import ( "context" "sync" + "sync/atomic" "time" "git.zetit.ru/zuevav/Bridge-and-Join-s/internal/cryptocli" @@ -40,13 +41,18 @@ func setupProbes(rc *RuntimeConfig) (string, int) { return setupProbeCache.validata, setupProbeCache.certs } +// keyWizInstalled — мастер ключа дошёл до конца в этом запуске bj (ключ подключён, подпись подтверждена). +// Сверка справочника шлюза асинхронная и отстаёт; по этой отметке шаг «Ключ подписи» выполнен сразу — +// и в мастере, и в плашке на главной. +var keyWizInstalled atomic.Bool + // setupSteps — шаги мастера этой установки и выполнены ли они: ровно тот путь и те же // правила, что в мастере (wizPath + wizDone), чтобы плашка на главной не расходилась с ним. func setupSteps(rc *RuntimeConfig) []SetupStep { validata, certs := setupProbes(rc) ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() - done := wizDone(ctx, rc, validata, certs > 0) + done := wizDone(ctx, rc, validata, certs > 0 || keyWizInstalled.Load()) path, _ := wizPath(rc, done, 0) var steps []SetupStep for _, n := range path { diff --git a/internal/lkgateway/web/templates/admin_wizard.html b/internal/lkgateway/web/templates/admin_wizard.html index 647ec98..ca37a7b 100644 --- a/internal/lkgateway/web/templates/admin_wizard.html +++ b/internal/lkgateway/web/templates/admin_wizard.html @@ -85,7 +85,7 @@
Ключ продукта открывает модули bj и обновления.
Есть новая версия bj — {{.Update.Available}}
-С исправлениями ядра, установщика и служб. Поставьте её до настройки: bj перезапустится примерно на 10 секунд.
{{.Update.Message}}
Поставьте её до настройки — следующий шаг «Обновление».
+ К обновлению →Модуль обмена с DEPO и QUIK не входит в текущую лицензию — шаг пропускается.
{{else}} @@ -334,7 +331,7 @@Учёт DEPO подключён и проверен. Дальше — «Организация»: bj предложит реквизиты вашей организации из DEPO.
{{else}}Не всё проверено — можно продолжить настройку (шаг 4 и дальше) и вернуться сюда позже. Пока обработка не работает, записи bj ждут в таблицах обмена со статусом TODO и будут обработаны, когда она заработает.
{{end}} + {{if .Done.Depo}}Учёт DEPO подключён и проверен. Дальше — «Организация»: bj предложит реквизиты вашей организации из DEPO.
{{else}}Не всё проверено — можно продолжить настройку (шаг «Организация» и дальше) и вернуться сюда позже. Пока обработка не работает, записи bj ждут в таблицах обмена со статусом TODO и будут обработаны, когда она заработает.
{{end}} {{end}} {{end}} @@ -344,7 +341,7 @@ {{/* ============= ШАГ 2: База bj (PostgreSQL) ============= */}} {{if eq .Step 2}}База самого bj: журнал заявок и поручений, история обмена с НРД и DEPO, пользователи.
{{if and .Done.Postgres (pgLocal .Settings.Postgres.DSN)}}От имени вашей организации bj отправляет и принимает переводы. {{if .DepoLicensed}}Реквизиты bj берёт из DEPO — проверьте и сохраните.{{else}}Найдите себя в справочнике участников НРД.{{end}}
{{if .Suggest}}{{if .Suggest.OrgName}}{{if not .Done.Org}}Адрес и логин модуля неторговых поручений — у вашего администратора QUIK. Если модуля ещё нет, его подключает ARQA Technologies — письмо для них готово ниже.
{{if .DepoLicensed}}{{template "letterCard" (letter "arqa")}}{{end}} {{if not .DepoLicensed}}Модуль неторговых поручений не входит в текущую лицензию — шаг пропускается.
@@ -455,7 +452,7 @@ {{/* ============= ШАГ 6: Валидата ============= */}} {{if eq .Step 6}}СКЗИ «Валидата Клиент L» проверяет подписи НРД на квитанциях и сообщениях и нужно для работы с ключами и сертификатами (раздел «Ключ»).
{{if .ValidataVer}}/opt/Validata/VDCSPbj сам ставит и настраивает шлюз (ИШ) и обменивается через него переводами с НРД. Доступ к контурам НРД — по заявке (как подать — ниже).
{{template "igateInstall" .}}Ключ электронной подписи организации для обмена с НРД. Его выдаёт Удостоверяющий центр Московской биржи: вам передадут архив ключа и пароль.
{{if not .IgateOK}}Часовой пояс, точное время и адрес сервера в сети.
{{$tz := serverTimeZone}}