Files
Bridge-and-Join-s/internal/lkgateway/web/templates/admin_wizard.html
T
zuevav 775b4214b6 Обновление с индикатором хода (этапы, скачанные МБ, перезапуск ядра); в мастере ключ продукта не просится повторно
- /admin/setup/update/progress: этап, байты, целевая версия ядра; ApplyUpdate/ApplyBundle отчитываются;
- окно в layout: формы установки обновлений отправляются скриптом, окно ждёт перезапуска ядра и сверяет версию;
- шаг 1 мастера: «Ключ продукта введён», смена ключа — под ссылкой.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
2026-09-24 22:04:08 +03:00

342 lines
38 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{define "content"}}
<style>
.wiz-head { text-align:center; padding:8px 0 4px; }
.wiz-head h1 { font-size:26px; font-weight:720; letter-spacing:-0.02em; margin:0 0 6px; }
.wiz-head p { color:var(--muted); margin:0; }
.wizard-progress { display:flex; align-items:center; justify-content:center; gap:0; margin:24px auto 28px; max-width:680px; }
.wstep { display:flex; flex-direction:column; align-items:center; gap:7px; flex:1; position:relative; }
.wstep .bub { width:34px; height:34px; border-radius:50%; display:flex; align-items:center; justify-content:center; font-size:14px; font-weight:650; background:var(--card-2); border:1.5px solid var(--border-strong); color:var(--muted); z-index:1; transition:all .2s; }
.wstep .lbl { font-size:12px; color:var(--muted); font-weight:550; }
.wstep::before { content:""; position:absolute; top:17px; left:-50%; width:100%; height:2px; background:var(--border); z-index:0; }
.wstep:first-child::before { display:none; }
.wstep.done .bub { background:var(--ok); border-color:var(--ok); color:#fff; }
.wstep.done .lbl { color:var(--text-2); }
.wstep.done::before { background:var(--ok); }
.wstep.current .bub { background:var(--accent); border-color:var(--accent); color:#fff; box-shadow:0 0 0 4px var(--accent-weak); }
.wstep.current .lbl { color:var(--accent); font-weight:650; }
.tooltip { display:inline-block; background:var(--border); color:var(--muted); border-radius:50%; width:16px; height:16px; line-height:16px; text-align:center; font-size:11px; cursor:help; margin-left:4px; }
.where { font-size:12px; color:var(--accent); margin-left:8px; }
.help-block { background:var(--accent-weak); background:var(--accent-weak); padding:10px 14px; margin:10px 0; font-size:13px; border-radius:0 8px 8px 0; }
.help-block strong { color:var(--accent); }
.wiz-card { max-width:680px; margin:0 auto; }
</style>
<div class="wiz-head">
<h1>Настройка Bridge&amp;Join</h1>
<p>Пошаговый мастер первого запуска — состояние сохраняется после каждого шага</p>
</div>
<div class="wizard-progress" style="max-width:860px">
<div class="wstep {{if .Done.License}}done{{end}} {{if eq .Step 1}}current{{end}}"><span class="bub">{{if .Done.License}}✓{{else}}1{{end}}</span><span class="lbl">Лицензия</span></div>
<div class="wstep {{if .Done.Postgres}}done{{end}} {{if eq .Step 2}}current{{end}}"><span class="bub">{{if .Done.Postgres}}✓{{else}}2{{end}}</span><span class="lbl">База bj</span></div>
<div class="wstep {{if .Done.Depo}}done{{end}} {{if eq .Step 3}}current{{end}}"><span class="bub">{{if .Done.Depo}}✓{{else}}3{{end}}</span><span class="lbl">Учёт DEPO</span></div>
<div class="wstep {{if .Done.Crypto}}done{{end}} {{if eq .Step 4}}current{{end}}"><span class="bub">{{if .Done.Crypto}}✓{{else}}4{{end}}</span><span class="lbl">Валидата</span></div>
<div class="wstep {{if .Done.Certs}}done{{end}} {{if eq .Step 5}}current{{end}}"><span class="bub">{{if .Done.Certs}}✓{{else}}5{{end}}</span><span class="lbl">Сертификаты</span></div>
<div class="wstep {{if .Done.NSD}}done{{end}} {{if eq .Step 6}}current{{end}}"><span class="bub">{{if .Done.NSD}}✓{{else}}6{{end}}</span><span class="lbl">Шлюз НРД</span></div>
<div class="wstep {{if .Done.TestRun}}done{{end}} {{if eq .Step 7}}current{{end}}"><span class="bub">{{if .Done.TestRun}}✓{{else}}7{{end}}</span><span class="lbl">Проверка</span></div>
</div>
<p style="text-align:center;margin:-14px 0 18px;font-size:13px"><a href="/admin/">Пропустить мастер и перейти в систему →</a> <span class="muted">— вернуться к нему можно из «Настройка → Мастер настройки»</span></p>
{{if .Flash}}<div class="card wiz-card" style="background:var(--accent-weak)"><p style="margin:0">{{.Flash}}</p></div>{{end}}
<div class="wiz-card">
{{/* ============= ШАГ 1: Лицензия ============= */}}
{{if eq .Step 1}}
<div class="card">
<h2><span class="dot {{if .Done.License}}ok{{else}}err{{end}}"></span>Шаг 1. Лицензия</h2>
<div class="help-block"><strong>Что это.</strong> Ключ продукта (BJ-XXXX-XXXX-XXXX) привязывает этот сервер к вашей лицензии.<br>
<strong>Зачем.</strong> От лицензии зависят модули (обмен с DEPO и QUIK, личный кабинет, НПР/УДС) и получение обновлений: bj скачает и поставит модули, входящие в лицензию, автоматически.<br>
<strong>Где взять.</strong> Портал клиента <a href="https://lic.zetit.ru/portal/" target="_blank">lic.zetit.ru/portal</a> или письмо от ZETIT.</div>
{{if .Done.License}}<p>✓ <strong>Ключ продукта введён</strong>, лицензия активна: <strong>{{.Lic.Tenant}}</strong>, план {{.Lic.Plan}}, модули: {{if .Lic.Features}}{{range $i, $f := .Lic.Features}}{{if $i}}, {{end}}{{$f}}{{end}}{{else}}только ядро{{end}}; {{.Lic.Message}}</p>{{end}}
{{with .Update}}{{if .HasUpdate}}
<div style="border:1px solid var(--accent);border-radius:8px;padding:12px 14px;margin-top:12px;background:var(--accent-weak)">
<p style="margin:0 0 6px;font-weight:600">На сервере лицензий есть что поставить (выпуск {{.Available}})</p>
<p class="muted" style="margin:0 0 10px;font-size:13px">Образ установил ту версию bj, с которой был собран. На сервере лицензий есть более новая — с исправлениями ядра, установщика и служб. Поставьте её сейчас, до настройки: bj перезапустится примерно на 10 секунд, затем обновите страницу.<br>{{.Message}}</p>
<form method="post" action="/admin/setup/update/apply" style="margin:0">
<input type="hidden" name="return_to" value="/admin/wizard?step=1">
<button type="submit" class="btn">Обновить сейчас</button>
</form>
</div>
{{else}}<p class="muted" style="font-size:13px">✓ Установлена последняя версия bj ({{.Available}}).</p>{{end}}{{end}}
{{if .Done.License}}<details style="margin-top:12px"><summary style="cursor:pointer;color:var(--accent);font-size:13px">Сменить ключ продукта (обычно не нужно)</summary>
<form method="post" action="/admin/setup/license/activate" style="margin-top:12px;display:grid;gap:10px">
<input type="hidden" name="return_to" value="/admin/wizard?step=1">
<label>Ключ продукта</label>
<input type="text" name="code" placeholder="BJ-XXXX-XXXX-XXXX" autocomplete="off" style="padding:8px;text-transform:uppercase;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px">
<button type="submit" class="btn" style="justify-self:start">{{if .Done.License}}Сменить ключ{{else}}Активировать{{end}}</button>
</form>
</details>{{else}}
<form method="post" action="/admin/setup/license/activate" style="margin-top:12px;display:grid;gap:10px">
<input type="hidden" name="return_to" value="/admin/wizard?step=1">
<label>Ключ продукта</label>
<input type="text" name="code" placeholder="BJ-XXXX-XXXX-XXXX" autocomplete="off" style="padding:8px;text-transform:uppercase;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px">
<button type="submit" class="btn" style="justify-self:start">{{if .Done.License}}Сменить ключ{{else}}Активировать{{end}}</button>
</form>{{end}}
<div style="margin-top:20px;display:flex;justify-content:space-between"><span></span>
{{if .Done.License}}<a href="/admin/wizard?step=2" class="btn" style="text-decoration:none">К шагу 2 →</a>{{else}}<a href="/admin/wizard?step=2" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);text-decoration:none">Пропустить (ввести позже) →</a>{{end}}</div>
</div>
{{end}}
{{/* ============= ШАГ 3: Учёт DEPO (Fancy) ============= */}}
{{if eq .Step 3}}
<div class="card">
<h2><span class="dot {{if .Done.Depo}}ok{{else}}err{{end}}"></span>Шаг 3. Учётная система DEPO (Fancy)</h2>
<div class="help-block"><strong>Что это.</strong> Подключение к базе депозитарного учёта DEPO (MS SQL / PostgreSQL).<br>
<strong>Зачем.</strong> bj сверяет с DEPO каждый перевод (клиент, счёт, остаток, квалификация) и передаёт в DEPO поручения и их исполнение — документы создаются в учёте автоматически. Отсюда же bj возьмёт ваши реквизиты в НРД (шаг 6), чтобы не вводить их вручную.<br>
<strong>Что нужно.</strong> Адрес сервера БД и логин с правами чтения DEPO и записи в трансферные таблицы FTV_M2M (выдаёт администратор СУБД).</div>
{{if not .DepoLicensed}}
<p class="muted">Модуль обмена с DEPO и QUIK не входит в текущую лицензию — шаг пропускается. После добавления модуля в лицензию он станет доступен здесь и в «Настройка → Обмен Fancy».</p>
{{else}}
{{if .Done.Depo}}<p>✓ Подключение сохранено: {{.Settings.Fancy.Server}}, база {{.DepoDB}}. {{if .Settings.Fancy.DepoTablesCheckOK}}Трансферные таблицы на месте.{{else}}Трансферные таблицы ещё не проверены — «Настройка → Обмен Fancy → Трансферные таблицы DEPO».{{end}}</p>{{end}}
<form method="post" action="/admin/setup/fancy" style="margin-top:12px;display:grid;gap:10px">
<input type="hidden" name="return_to" value="/admin/wizard?step=3">
<div><label>Тип СУБД</label><select name="db_type" style="padding:8px;width:100%;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px"><option value="mssql" {{if ne .Settings.Fancy.DBType "postgres"}}selected{{end}}>MS SQL Server</option><option value="postgres" {{if eq .Settings.Fancy.DBType "postgres"}}selected{{end}}>PostgreSQL</option></select></div>
<div><label>Сервер (адрес или host\instance)</label><input type="text" name="server" value="{{.Settings.Fancy.Server}}" placeholder="10.0.0.21" style="padding:8px;width:100%;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px"></div>
<div><label>Порт</label><input type="text" name="port" value="{{if .Settings.Fancy.Port}}{{.Settings.Fancy.Port}}{{else}}1433{{end}}" style="padding:8px;width:100%;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px"></div>
<div><label>Логин БД</label><input type="text" name="user" value="{{.Settings.Fancy.User}}" autocomplete="off" style="padding:8px;width:100%;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px"></div>
<div><label>Пароль {{if .Settings.Fancy.Server}}<span class="muted">(пусто — оставить прежний)</span>{{end}}</label><input type="password" name="password" autocomplete="new-password" style="padding:8px;width:100%;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px"></div>
<input type="hidden" name="encrypt" value="{{if .Settings.Fancy.Encrypt}}{{.Settings.Fancy.Encrypt}}{{else}}disable{{end}}">
<div style="display:flex;gap:10px;flex-wrap:wrap"><button type="submit" class="btn">Сохранить</button>
{{if .Settings.Fancy.Server}}<button type="submit" class="btn" formaction="/admin/setup/fancy/test" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8)">Проверить соединение</button>{{end}}</div>
</form>
{{end}}
<div style="margin-top:20px;display:flex;justify-content:space-between">
<a href="/admin/wizard?step=2" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);text-decoration:none">← К шагу 2</a>
<a href="/admin/wizard?step=4" class="btn" style="{{if not .Done.Depo}}background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);{{end}}text-decoration:none">{{if .Done.Depo}}К шагу 4 →{{else}}Пропустить →{{end}}</a></div>
</div>
{{end}}
{{/* ============= ШАГ 1: PostgreSQL ============= */}}
{{if eq .Step 2}}
<div class="card">
<h2><span class="dot {{if .Done.Postgres}}ok{{else}}err{{end}}"></span>Шаг 2. База данных bj (PostgreSQL)</h2>
<p>База самого bj: журнал заявок и поручений, история обмена с НРД и DEPO, пользователи.</p>
{{if .Done.Postgres}}<p>✓ База готова{{if .PGSummary}}: {{.PGSummary}}{{end}}. Её создал установщик — ничего делать не нужно, переходите к следующему шагу.</p>{{end}}
{{if not .Done.Postgres}}<div class="help-block">
<strong>Что выбрать?</strong> Если у вас уже есть рабочий PostgreSQL — нажмите «У меня уже есть PostgreSQL» и введите DSN. Если впервые настраиваете — выберите «Поднять автоматически», система сама развернёт контейнер с PostgreSQL и накатит миграции.
</div>{{end}}
{{if and devOn (not .Settings.Postgres.DSN)}}
<div style="background:var(--bg);border:1px solid var(--accent);border-radius:6px;padding:14px;margin-top:12px">
<h3 style="margin:0 0 8px 0;font-size:15px">Вариант А — для тех, у кого нет своего PostgreSQL</h3>
<p class="muted" style="margin:0 0 10px 0">Bridge-and-Join-s сам поднимет PostgreSQL в контейнере (podman-compose), создаст БД <code>bj</code> и накатит миграции. Подходит для дев-стенда. Для продакшена лучше указать свой DSN.</p>
<form method="post" action="/admin/setup/postgres/quick-start" style="margin:0">
<button type="submit" class="btn" style="background:var(--ok)">Поднять локальный PostgreSQL автоматически</button>
<span class="muted" style="margin-left:10px;font-size:12px">~10-30 сек</span>
</form>
</div>
{{end}}
<details style="margin-top:14px" {{if not .Settings.Postgres.DSN}}open{{end}}>
<summary style="cursor:pointer;color:var(--accent)">{{if .Settings.Postgres.DSN}}Подключить другую базу (обычно не нужно){{else}}Вариант Б — у меня уже есть PostgreSQL, введу DSN сам{{end}}</summary>
<form method="post" action="/admin/setup/postgres" style="margin-top:12px">
<label>DSN (строка подключения) <span class="tooltip" title="Формат: postgres://пользователь:пароль@хост:порт/база?sslmode=disable. Например: postgres://bj:secret@db.example.com:5432/bj?sslmode=require">?</span></label>
<input type="text" name="dsn" value="{{.Settings.Postgres.DSN}}" placeholder="postgres://bj:secret@127.0.0.1:5432/bj?sslmode=disable" style="width:100%;padding:8px;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px;margin-top:6px">
<p class="muted" style="margin-top:8px">При сохранении выполняется проверка подключения (Ping). Если БД недоступна — будет ошибка.</p>
<button type="submit" class="btn" style="margin-top:8px">Сохранить и проверить</button>
</form>
</details>
<div style="margin-top:20px;display:flex;justify-content:space-between">
<a href="/admin/wizard?step=1" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);text-decoration:none">← К шагу 1</a>
{{if .Done.Postgres}}<a href="/admin/wizard?step=3" class="btn" style="text-decoration:none">К шагу 3 →</a>{{else}}<button class="btn" disabled style="opacity:0.5;cursor:not-allowed">К шагу 2 → (сначала настройте PostgreSQL или нажмите «in-memory режим»)</button>{{end}}
</div>
{{if not .Done.Postgres}}<p style="margin-top:8px"><a href="/admin/wizard?step=3&skip=postgres" style="font-size:13px">Пропустить (буду работать в режиме in-memory — без сохранения сделок)</a></p>{{end}}
</div>
{{end}}
{{/* ============= ШАГ 2: Крипто (Валидата) ============= */}}
{{if eq .Step 4}}
<div class="card">
<h2><span class="dot {{if .Done.Crypto}}ok{{else}}err{{end}}"></span>Шаг 4. СКЗИ «Валидата Клиент L»</h2>
<p>СКЗИ «Валидата Клиент L» проверяет подписи НРД на квитанциях и сообщениях и нужно для работы с ключами и сертификатами (шаг 5).</p>
{{if .ValidataVer}}
<p>✓ Валидата установлена вместе с bj: версия {{.ValidataVer}}, каталог <code>/opt/Validata/VDCSP</code>. Ничего настраивать на этом шаге не нужно.</p>
<p class="muted" style="font-size:13px">bj работает с Валидатой через свою службу криптографии <code>bj-crypto</code>{{if eq .CryptoSvc "active"}} — она запущена{{else}} — сейчас она не запущена: служба ставится обновлением с сервера лицензий (шаг 1, кнопка «Обновить сейчас»), а работает с ключом, установленным на шаге 5{{end}}.</p>
{{else}}
<p style="color:var(--err)">Валидата на этом сервере не найдена.</p>
<div class="help-block">
<strong>Как поставить.</strong> С образа Bridge&amp;Join она ставится сама. Если bj ставили на готовую систему: пакеты <code>zpki</code> и <code>zsdk</code> из дистрибутива НРД (<a href="https://fs.moex.com/cdp/po/ClientL_Other.zip" target="_blank">ClientL_Other.zip</a> для Ubuntu 22.04, <a href="https://fs.moex.com/cdp/po/ClientL_ALSE.zip" target="_blank">ClientL_ALSE.zip</a> для Astra Linux) — <code>sudo apt install ./zpki_*.deb ./zsdk_*.deb</code>, подробнее — <a href="/admin/help/crypto">справка</a>. После установки обновите эту страницу.
</div>
{{if devOn}}<form method="post" action="/admin/setup/crypto" style="margin-top:12px">
<input type="hidden" name="provider" value="stub">
<button type="submit" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8)">Режим разработки: работать без криптографии (stub)</button>
</form>{{end}}
{{end}}
<div style="margin-top:20px;display:flex;justify-content:space-between">
<a href="/admin/wizard?step=3" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);text-decoration:none">← К шагу 3</a>
{{if .Done.Crypto}}<a href="/admin/wizard?step=5" class="btn" style="text-decoration:none">К шагу 5 →</a>{{else}}<a href="/admin/wizard?step=5&skip=crypto" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);text-decoration:none">Пропустить →</a>{{end}}
</div>
</div>
{{end}}
{{/* ============= ШАГ 3: Сертификаты ============= */}}
{{if eq .Step 5}}
<div class="card">
<h2><span class="dot {{if .Done.Certs}}ok{{else}}err{{end}}"></span>Шаг 5. Сертификаты</h2>
<p>Здесь две части: ключ и сертификат вашей организации (ставите вы) и сертификаты НРД и удостоверяющих центров (bj ведёт их сам).</p>
<h3 style="margin-top:14px">1. Ключ и сертификат организации</h3>
<div class="help-block"><strong>Что это.</strong> Ключ ЭП, которым подписываются сообщения в НРД, и его сертификат (выдаёт УЦ Московской биржи). Архив ключа с паролем вам передаёт НРД/УЦ.<br>
<strong>Как поставить.</strong> Мастер установки ключа: загрузить архив → запись на носитель → справочник сертификатов → проверка подписи Валидатой.</div>
<a href="/admin/setup/keywizard" class="btn" style="text-decoration:none;display:inline-block">Открыть мастер установки ключа →</a>
<h3 style="margin-top:22px">2. Сертификаты НРД и УЦ — автоматически</h3>
<p>✓ <strong>Автоматическое обновление {{if .Settings.NSDCerts.Manual}}<span style="color:var(--warn)">выключено</span> (включается в «Настройка → Криптография»){{else}}включено{{end}}.</strong> Делать на этом шаге ничего не нужно.</p>
<div class="help-block" style="font-size:13px">
<strong>Как это работает.</strong> Действующие сертификаты НРД и удостоверяющих центров Московской биржи поставляются вместе с bj и приезжают с обновлениями. Каждые 6 часов bj сверяет их со справочником сертификатов ИШ: добавляет недостающие, а заменённые НРД убирает строго после срока, который НРД объявил в уведомлении. Перед каждым изменением делается резервная копия и проверка подписи, при ошибке всё откатывается.<br>
<strong>Тестовые и боевые.</strong> Сертификат попадает только в справочник своего контура: боевые (издатель «Корневой УЦ») — только к боевому ключу, тестовые (издатель «Тестовый УЦ») — только к тестовому. Поэтому смешать контуры нельзя.<br>
<strong>Откуда берутся.</strong> НРД и Московская биржа публикуют сертификаты в постоянных местах: комплект ГОСТ МБ, НКЦ и НРД — <code>https://fs.moex.com/cdp/sert/GOST.zip</code>, сертификаты УЦ — <code>https://fs.moex.com/cdp/</code>. Когда там появляется новый сертификат, мы добавляем его в комплект bj, и он приезжает к вам с обновлением.<br>
Состояние, ручная проверка и выключение — «Настройка → Криптография → Сертификаты НРД в справочнике ИШ».
</div>
{{if .Settings.NSDCerts.LastRows}}<p class="muted" style="font-size:13px">Последняя сверка: {{.Settings.NSDCerts.LastRun.Format "02.01.2006 15:04"}}{{if .Settings.NSDCerts.LastProfile}}, профиль ИШ {{.Settings.NSDCerts.LastProfile}}{{end}}.</p>{{else}}<p class="muted" style="font-size:13px">Первая сверка пройдёт, когда будут настроены ключ и шлюз НРД (шаг 6).</p>{{end}}
{{if .Certs}}
<h3 style="margin-top:18px">Установленные сертификаты ({{len .Certs}})</h3>
<table>
<thead><tr><th>Владелец</th><th>Издатель</th><th>Действителен до</th><th>ИНН</th><th>Ключ</th></tr></thead>
<tbody>
{{range .Certs}}
<tr>
<td>{{.SubjectCN}}</td>
<td>{{.IssuerCN}}</td>
<td>{{.NotAfter.Format "02.01.2006"}}</td>
<td>{{if .INN}}<code>{{.INN}}</code>{{else}}—{{end}}</td>
<td>{{if .HasPrivateKey}}<span style="color:var(--ok)">есть</span>{{else}}<span class="muted">нет</span>{{end}}</td>
</tr>
{{end}}
</tbody>
</table>
{{else}}
<p class="muted" style="margin-top:12px">Ключ организации ещё не установлен. Можно пропустить этот шаг и вернуться к нему, когда будет архив ключа.</p>
{{end}}
<div style="margin-top:20px;display:flex;justify-content:space-between">
<a href="/admin/wizard?step=4" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);text-decoration:none">← К шагу 4</a>
<a href="/admin/wizard?step=6" class="btn" style="text-decoration:none">К шагу 6 →</a>
</div>
</div>
{{end}}
{{/* ============= ШАГ 4: НРД ============= */}}
{{if eq .Step 6}}
<div class="card">
<h2><span class="dot {{if .Done.NSD}}ok{{else}}err{{end}}"></span>Шаг 6. Интеграционный шлюз НРД</h2>
<p>Адрес web-сервиса ONYX и имя ключевого контейнера НРД.</p>
<div class="help-block">
<strong>Что это?</strong> Интеграционный шлюз (ИШ) НРД — это компонент, через который наши M2M-сообщения отправляются в НРД. {{if devOn}}У НРД есть 4 контура: <em>GUEST</em> (для разработки) и <em>TEST3</em> (предпродакшен), каждый в варианте ГОСТ или RSA.{{else}}У НРД есть тестовый контур (<em>TEST3</em>) и боевой контур, каждый в варианте ГОСТ или RSA.{{end}}<br>
<strong>Где взять?</strong> Дистрибутив ИШ и инструкции — на сайте НРД <a href="https://www.nsd.ru/workflow/system/programs/" target="_blank">nsd.ru/workflow/system/programs/</a>. Доступ к тестовым контурам выдаётся НРД по заявке (см. <code>DOC/instr_podkl_stend_v3.pdf</code>).
</div>
<form method="post" action="/admin/setup/nsd" style="margin-top:12px;display:grid;gap:10px">
<div>
<label>Профиль <span class="tooltip" title="{{if devOn}}GUEST — гостевой контур для разработчиков (gost-gt.nsd.ru), {{end}}TEST3 — тестовый контур НРД (gost-t3.nsd.ru), prod — боевой контур НРД">?</span></label>
<select name="profile" id="nsd-profile" style="padding:8px;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px;width:100%">
<option value="test3-gost" {{if eq .Settings.NSD.Profile "test3-gost"}}selected{{end}}>TEST3 · ГОСТ — тестовый контур НРД</option>
<option value="test3-rsa" {{if eq .Settings.NSD.Profile "test3-rsa"}}selected{{end}}>TEST3 · RSA — тестовый контур НРД</option>
{{if or devOn (eq .Settings.NSD.Profile "guest-gost") (eq .Settings.NSD.Profile "guest-rsa")}} <option value="guest-gost" {{if eq .Settings.NSD.Profile "guest-gost"}}selected{{end}}>GUEST · ГОСТ</option>
<option value="guest-rsa" {{if eq .Settings.NSD.Profile "guest-rsa"}}selected{{end}}>GUEST · RSA</option>{{end}}
<option value="prod" {{if eq .Settings.NSD.Profile "prod"}}selected{{end}}>prod — боевой контур НРД (осторожно)</option>
</select>
</div>
<div>
<label>URL ONYX <span class="tooltip" title="Базовый URL веб-сервиса ONYX. При выборе профиля выше — заполняется автоматически.">?</span></label>
<input type="text" name="igw_base_url" id="nsd-url" value="{{.Settings.NSD.IGWBaseURL}}" placeholder="https://gost-t3.nsd.ru/onyx-ms/OnyxEdoWSService/OnyxEdo" style="padding:8px;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px;width:100%">
</div>
<div>
<label>Ключевой контейнер НРД <span class="tooltip" title="Имя контейнера Валидаты с ключами ЭДО НРД (выдаются УЦ НРД)">?</span></label>
<input type="text" name="key_container" value="{{.Settings.NSD.KeyContainer}}" placeholder="nrd-edo" style="padding:8px;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px;width:100%">
</div>
<hr style="border:none;border-top:1px solid var(--border);margin:4px 0">
<p class="muted" style="margin:0">Депозитарные реквизиты организации в НРД. {{if .Settings.Fancy.Server}}Их можно взять из базы DEPO (Fancy): кнопка «Определить из DEPO» подставит найденное в пустые поля — проверьте и сохраните.{{else}}Если учёт ведётся в Fancy DEPO — сначала подключите базу DEPO на <a href="/admin/wizard?step=3">шаге 3</a>, тогда реквизиты подставятся из DEPO автоматически; иначе введите их из договора с НРД.{{end}}</p>
{{if .Settings.Fancy.Server}}<button type="submit" class="btn" formaction="/admin/setup/fancy/detect" formmethod="post" style="justify-self:start;background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8)">Определить из DEPO</button>{{end}}
{{if .Suggest}}{{if .Suggest.AccountID}}<div class="help-block" style="margin:0"><strong>Найдено в DEPO:</strong> счёт {{.Suggest.AccountID}}, код {{.Suggest.DeponentCode}}, раздел {{.Suggest.SectionID}}{{if .Suggest.OrgName}}; организация {{.Suggest.OrgName}}, ИНН {{.Suggest.OrgINN}}{{end}}.{{range .Suggest.Notes}}<br><span class="muted">⚠ {{.}}</span>{{end}}</div>{{end}}{{end}}
<div><label>Депозитарный код</label><input type="text" name="deponent_code" value="{{if .Settings.NSD.DeponentCode}}{{.Settings.NSD.DeponentCode}}{{else if .Suggest}}{{.Suggest.DeponentCode}}{{end}}" placeholder="MC0000000000" style="padding:8px;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px;width:100%"></div>
<div><label>Депозитарный счёт</label><input type="text" name="account_id" value="{{if .Settings.NSD.AccountID}}{{.Settings.NSD.AccountID}}{{else if .Suggest}}{{.Suggest.AccountID}}{{end}}" placeholder="HL000000000C" style="padding:8px;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px;width:100%"></div>
<div><label>Раздел счёта</label><input type="text" name="section_id" value="{{if .Settings.NSD.SectionID}}{{.Settings.NSD.SectionID}}{{else if .Suggest}}{{.Suggest.SectionID}}{{end}}" placeholder="36MC0000000000F00" style="padding:8px;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px;width:100%"></div>
<button type="submit" class="btn" style="justify-self:start">Сохранить</button>
</form>
{{if .Suggest}}{{if .Suggest.OrgName}}<form method="post" action="/admin/setup/org" style="margin-top:12px;display:grid;gap:10px">
<p class="muted" style="margin:0">Организация (из DEPO — проверьте): код участника, ИНН, наименование.</p>
<input type="hidden" name="return_to" value="/admin/wizard?step=6">
<div><label>Код участника</label><input type="text" name="org_code" value="{{if .Settings.NSD.OrgCode}}{{.Settings.NSD.OrgCode}}{{else}}{{.Suggest.OrgCode}}{{end}}" style="padding:8px;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px;width:100%"></div>
<div><label>ИНН</label><input type="text" name="org_inn" value="{{if .Settings.NSD.OrgINN}}{{.Settings.NSD.OrgINN}}{{else}}{{.Suggest.OrgINN}}{{end}}" style="padding:8px;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px;width:100%"></div>
<div><label>Наименование</label><input type="text" name="org_name" value="{{if .Settings.NSD.OrgName}}{{.Settings.NSD.OrgName}}{{else}}{{.Suggest.OrgName}}{{end}}" style="padding:8px;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px;width:100%"></div>
<button type="submit" class="btn" style="justify-self:start">Сохранить организацию</button>
</form>{{end}}{{end}}
<script>
// Автозаполнение URL по выбранному профилю
document.getElementById('nsd-profile').addEventListener('change', function(e){
var urls = {
'test3-gost': 'https://gost-t3.nsd.ru/onyx-ms/OnyxEdoWSService/OnyxEdo',
'test3-rsa': 'https://rsa-t3.nsd.ru/onyx-ms/OnyxEdoWSService/OnyxEdo',
'guest-gost': 'https://gost-gt.nsd.ru/onyx-ms/OnyxEdoWSService/OnyxEdo',
'guest-rsa': 'https://rsa-gt.nsd.ru/onyx-ms/OnyxEdoWSService/OnyxEdo',
'prod': ''
};
var u = document.getElementById('nsd-url');
if (urls[e.target.value]) u.value = urls[e.target.value];
});
</script>
<div style="margin-top:20px;display:flex;justify-content:space-between">
<a href="/admin/wizard?step=5" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);text-decoration:none">← К шагу 5</a>
{{if .Done.NSD}}<a href="/admin/wizard?step=7" class="btn" style="text-decoration:none">К шагу 7 →</a>{{else}}<a href="/admin/wizard?step=7&skip=nsd" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);text-decoration:none">Пропустить ({{if devOn}}mock-режим{{else}}обмен с НРД не настроен{{end}}) →</a>{{end}}
</div>
</div>
{{end}}
{{/* ============= ШАГ 5: Тест-ран (режим разработки) / проверка подключений ============= */}}
{{if eq .Step 7}}
<div class="card">
{{if devOn}}
<h2><span class="dot {{if .Done.TestRun}}ok{{else}}err{{end}}"></span>Шаг 7. Тестовая заявка</h2>
<p>Прогон полного цикла: создание заявки → валидация → подпись → отправка в НРД (или mock) → ожидание Decision → подтверждение.</p>
<div class="help-block">
<strong>Что произойдёт?</strong> Система создаст тестовую M2M-сделку, проведёт её через всю стейт-машину, и покажет результат каждого этапа. Если ИШ НРД не настроен — сработает mock (синтетический Decision через 3 секунды).
</div>
<form method="post" action="/admin/setup/test-run" style="margin-top:12px">
<button type="submit" class="btn" style="background:var(--ok);font-size:15px;padding:10px 20px">▶ Запустить тестовую заявку</button>
</form>
{{if .Settings.LastTest}}
<h3 style="margin-top:18px">Последний прогон: {{.Settings.LastTest.StartedAt.Format "02.01.2006 15:04:05"}}</h3>
<table>
<tr><td class="muted">Заявка</td><td><a href="/admin/claims/{{.Settings.LastTest.ClaimID}}">{{.Settings.LastTest.ClaimID}}</a></td></tr>
<tr><td class="muted">Финальное состояние</td><td>{{ruState .Settings.LastTest.FinalStatus}}</td></tr>
<tr><td class="muted">Результат</td><td>{{if .Settings.LastTest.OK}}<span style="color:var(--ok)">успех</span>{{else}}<span style="color:var(--err)">ошибка</span>{{end}}</td></tr>
{{if .Settings.LastTest.Message}}<tr><td class="muted">Сообщение</td><td>{{.Settings.LastTest.Message}}</td></tr>{{end}}
</table>
{{end}}
{{else}}
<h2><span class="dot {{if .Done.TestRun}}ok{{else}}err{{end}}"></span>Шаг 7. Проверка подключений</h2>
<p>Проверьте, что база данных, СКЗИ и шлюз НРД доступны: на странице «Статус» bj проверяет каждое подключение и показывает, что нужно исправить.</p>
<a href="/admin/status" class="btn" style="background:var(--ok);font-size:15px;padding:10px 20px;text-decoration:none;display:inline-block;margin-top:12px">Открыть проверку подключений →</a>
{{end}}
<h3 style="margin-top:18px">Итоговая сводка</h3>
<table>
<tr><td class="muted">PostgreSQL</td><td>{{if .Done.Postgres}}<span style="color:var(--ok)">настроен</span>{{else}}<span class="muted">in-memory</span>{{end}}</td></tr>
<tr><td class="muted">Крипто-провайдер</td><td>{{if .Done.Crypto}}<span style="color:var(--ok)">Валидата Клиент L{{if .ValidataVer}} {{.ValidataVer}}{{end}}</span>{{else}}<span style="color:var(--err)">не настроен</span>{{end}}</td></tr>
<tr><td class="muted">Сертификатов установлено</td><td>{{len .Certs}}</td></tr>
<tr><td class="muted">ИШ НРД</td><td>{{if .Done.NSD}}<span style="color:var(--ok)">{{.Settings.NSD.Profile}}</span>{{else}}<span class="muted">{{if devOn}}mock-режим{{else}}не настроен{{end}}</span>{{end}}</td></tr>
{{if devOn}}<tr><td class="muted">Тестовый прогон</td><td>{{if .Done.TestRun}}<span style="color:var(--ok)">пройден</span>{{else}}<span class="muted">не запускался</span>{{end}}</td></tr>{{end}}
</table>
<div style="margin-top:20px;display:flex;justify-content:space-between">
<a href="/admin/wizard?step=6" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);text-decoration:none">← К шагу 6</a>
<a href="/admin/" class="btn" style="text-decoration:none">Перейти к дашборду</a>
</div>
</div>
{{end}}
</div>
{{end}}