ba1aae705d
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
600 lines
70 KiB
HTML
600 lines
70 KiB
HTML
{{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; }
|
||
a.wstep { text-decoration:none; cursor:pointer; }
|
||
a.wstep:hover .bub { box-shadow:0 0 0 4px var(--accent-weak); }
|
||
.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; min-width:0; }
|
||
.wiz-sub { display:flex; justify-content:center; gap:6px; flex-wrap:wrap; margin:-12px auto 18px; max-width:680px; }
|
||
.wiz-sub a { font-size:12.5px; padding:4px 12px; border-radius:14px; background:var(--card-2); color:var(--muted); text-decoration:none; border:1px solid var(--border); }
|
||
.wiz-sub a.done { color:var(--ok); }
|
||
.wiz-sub a.cur { background:var(--accent); color:#fff; border-color:var(--accent); }
|
||
.wiz-nav { margin-top:20px; display:flex; justify-content:space-between; gap:8px; flex-wrap:wrap; }
|
||
.wiz-card * { min-width:0; }
|
||
</style>
|
||
|
||
<div class="wiz-head">
|
||
<h1>Настройка Bridge&Join</h1>
|
||
<p>Четыре главы: сервер, учёт и организация, НРД, запуск. Всё сохраняется сразу — можно прерваться и вернуться</p>
|
||
</div>
|
||
|
||
<div class="wizard-progress" style="max-width:720px">
|
||
{{range .Chapters}}<a href="/admin/wizard?step={{.First}}" title="Глава {{.N}}: {{.Title}}" class="wstep {{if .Done}}done{{end}} {{if .Current}}current{{end}}"><span class="bub">{{if .Done}}✓{{else}}{{.N}}{{end}}</span><span class="lbl">{{.Title}}</span></a>
|
||
{{end}}
|
||
</div>
|
||
{{range .Chapters}}{{if and .Current (gt (len .Steps) 1)}}<div class="wiz-sub">{{range .Steps}}<a href="/admin/wizard?step={{.N}}" class="{{if .Current}}cur{{end}}{{if .Done}} done{{end}}">{{if .Done}}✓ {{end}}{{.Title}}</a>{{end}}</div>{{end}}{{end}}
|
||
<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" id="wiz-restore" style="border-style:dashed">
|
||
<details>
|
||
<summary style="cursor:pointer;font-weight:600">Переустановка сервера? Восстановить из резервной копии</summary>
|
||
<p class="muted" style="font-size:13px;margin:8px 0">Если у вас есть резервная копия bj на сетевом хранилище (Настройка → Резервное копирование), можно восстановить её вместо настройки с нуля: вернутся настройки, пользователи, лицензия, журнал заявок, база и папка шлюза НРД. Текущие данные этого сервера будут заменены. bj и шлюз НРД на время восстановления остановятся — обычно 1–3 минуты.</p>
|
||
<style>#rs-form label:not(.chk){display:block;margin:0}#rs-form label:not(.chk) input,#rs-form label:not(.chk) select{width:100%;box-sizing:border-box;margin-top:4px}#rs-pick label{flex:1;min-width:220px}</style>
|
||
<form id="rs-form" style="display:grid;gap:8px;max-width:640px" onsubmit="return false">
|
||
<div style="display:grid;grid-template-columns:2fr 1fr;gap:8px">
|
||
<label>Хранилище (SMB)<input name="share" placeholder="//nas/backup" value="{{.Settings.Backup.Share}}"></label>
|
||
<label>Папка<input name="dir" placeholder="bj-backup" value="{{.Settings.Backup.Dir}}"></label>
|
||
</div>
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px">
|
||
<label>Логин хранилища<input name="user" autocomplete="off" value="{{.Settings.Backup.User}}"></label>
|
||
<label>Пароль<input name="password" type="password" autocomplete="new-password"></label>
|
||
</div>
|
||
<div><button class="btn" type="button" id="rs-list">Показать копии</button></div>
|
||
<div id="rs-pick" style="display:none;gap:8px;align-items:flex-end;flex-wrap:wrap">
|
||
<label>Копия<select name="name" id="rs-name"></select></label>
|
||
<button class="btn" type="button" id="rs-go" style="background:var(--err);border-color:var(--err)">Восстановить</button>
|
||
</div>
|
||
<div id="rs-out" class="muted" style="font-size:13px"></div>
|
||
</form>
|
||
<script>
|
||
(function(){
|
||
var f=document.getElementById('rs-form'), out=document.getElementById('rs-out'), pick=document.getElementById('rs-pick'), sel=document.getElementById('rs-name');
|
||
function esc(s){return String(s).replace(/[&<>"]/g,function(c){return{'&':'&','<':'<','>':'>','"':'"'}[c]})}
|
||
function params(){ var p=new URLSearchParams(); ['share','dir','user','password'].forEach(function(n){ var v=f[n].value.trim(); if(v) p.set(n,v); }); return p; }
|
||
function nice(n){ var m=n.match(/^(\d{4})-(\d{2})-(\d{2})_(\d{2})(\d{2})$/); return m? m[3]+'.'+m[2]+'.'+m[1]+' '+m[4]+':'+m[5] : n; }
|
||
document.getElementById('rs-list').onclick=function(){
|
||
out.textContent='Читаю список копий…'; pick.style.display='none';
|
||
fetch('/admin/setup/backup/list',{method:'POST',body:params(),cache:'no-store'}).then(function(r){return r.json()}).then(function(d){
|
||
if(!d.ok){ out.innerHTML='<span style="color:var(--err)">✗ '+esc(d.error)+'</span>'; return; }
|
||
if(!d.list||!d.list.length){ out.textContent='На хранилище копий нет.'; return; }
|
||
sel.innerHTML=d.list.map(function(n){return '<option value="'+esc(n)+'">'+esc(nice(n))+'</option>'}).join('');
|
||
pick.style.display='flex'; out.textContent='Найдено копий: '+d.list.length+'. Выберите копию — обычно самую свежую.';
|
||
}).catch(function(){ out.textContent='Не удалось получить список.'; });
|
||
};
|
||
document.getElementById('rs-go').onclick=function(){
|
||
if(!confirm('Восстановить копию от '+nice(sel.value)+'? Текущие настройки и данные этого сервера будут заменены, bj и шлюз НРД перезапустятся.')) return;
|
||
var p=params(); p.set('name', sel.value); var t0=Date.now();
|
||
out.textContent='Запускаю восстановление…';
|
||
fetch('/admin/setup/backup/restore',{method:'POST',body:p,cache:'no-store'}).then(function(r){return r.json()}).then(function(d){
|
||
if(!d.ok){ out.innerHTML='<span style="color:var(--err)">✗ '+esc(d.error)+'</span>'; return; }
|
||
var iv=setInterval(function(){
|
||
fetch('/admin/setup/backup/status',{cache:'no-store'}).then(function(r){return r.json()}).then(function(s){
|
||
var r=s.restore; if(!r) return;
|
||
out.textContent='Восстановление: '+r.msg;
|
||
if(r.stage==='done'){ clearInterval(iv); out.innerHTML='<span style="color:var(--ok)">✓ '+esc(r.msg)+'. Сейчас откроется вход — войдите учётной записью из восстановленной копии.</span>'; setTimeout(function(){ location.href='/admin/'; }, 4000); }
|
||
if(r.stage==='error'){ clearInterval(iv); out.innerHTML='<span style="color:var(--err)">✗ '+esc(r.msg)+'</span>'; }
|
||
}).catch(function(){ out.textContent='bj перезапускается, жду…'; });
|
||
if(Date.now()-t0>900000) clearInterval(iv);
|
||
}, 3000);
|
||
});
|
||
};
|
||
})();
|
||
</script>
|
||
</details>
|
||
</div>
|
||
<div class="card">
|
||
<h2><span class="dot {{if .Done.License}}ok{{else}}err{{end}}"></span>Лицензия</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>, {{if eq .Lic.Plan "enterprise"}}полная лицензия (все модули){{else}}базовая лицензия{{end}}, модули: {{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}}
|
||
{{template "wizNav" .}}
|
||
</div>
|
||
{{end}}
|
||
|
||
{{/* ============= ШАГ 3: Учёт DEPO (Fancy) — по экранам ============= */}}
|
||
{{if eq .Step 3}}
|
||
<div class="card">
|
||
<h2><span class="dot {{if .Done.Depo}}ok{{else}}err{{end}}"></span>Учётная система DEPO (Fancy)</h2>
|
||
{{if not .DepoLicensed}}
|
||
<p class="muted">Модуль обмена с DEPO и QUIK не входит в текущую лицензию — шаг пропускается.</p>
|
||
{{else}}
|
||
{{$sub := .Sub}}
|
||
<div style="display:flex;gap:6px;flex-wrap:wrap;margin:6px 0 14px">{{range $i, $x := depoSubs}}<span style="font-size:12px;padding:4px 10px;border-radius:12px;{{if eq $x.Key $sub}}background:var(--accent);color:#fff{{else}}background:var(--card-2);color:var(--muted){{end}}">{{add $i 1}}. {{$x.Title}}</span>{{end}}</div>
|
||
|
||
{{if eq $sub "intro"}}
|
||
<div class="help-block"><strong>Что это.</strong> Подключение bj к базе депозитарного учёта DEPO (Fancy).<br>
|
||
<strong>Зачем.</strong> bj сверяет с учётом каждый перевод (клиент, счёт, остаток, квалификация) и сам передаёт в DEPO поручения и их исполнение — документы в учёте создаются автоматически. Отсюда же bj возьмёт реквизиты вашей организации (раздел «Организация»).<br>
|
||
<strong>Что понадобится.</strong> Адрес сервера базы DEPO, логин и пароль — их даёт ваш администратор баз данных. Для создания таблиц обмена и джобы обработки может понадобиться разово логин администратора SQL — он не сохраняется. В базе DEPO должны быть процедуры обработки обмена (расширение bj2depo) и версия Fancy DEPO с поддержкой M2M — bj проверит их по имени и скажет, чего не хватает.<br>
|
||
<strong>Как пойдём.</strong> Подключение → таблицы обмена → обработка → проверка. Каждый шаг проверяется сразу.</div>
|
||
{{template "letterCard" (letter "dba")}}
|
||
<div style="margin-top:14px"><a href="/admin/wizard?step=3&sub=conn" class="btn" style="text-decoration:none">Начать →</a></div>
|
||
|
||
{{else if eq $sub "conn"}}
|
||
<p>Адрес сервера учётной базы DEPO и логин, под которым bj будет с ней работать. По «Далее» bj сохранит и сразу проверит подключение.</p>
|
||
<form method="post" action="/admin/setup/wizard-depo/conn" style="display:grid;gap:10px;max-width:560px">
|
||
<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}}>Microsoft 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" required 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>База DEPO</label><input type="text" name="depo_database" value="{{.Settings.Fancy.DepoDatabase}}" placeholder="{{.Settings.Fancy.DepoDB}}" pattern="[A-Za-z][A-Za-z0-9_]*" 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" required 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>
|
||
<details id="wiz-login-help" style="border:1px solid var(--border);border-radius:8px;padding:8px 12px;background:var(--card-2)">
|
||
<summary style="cursor:pointer;font-weight:600">Логина для bj ещё нет? Как создать</summary>
|
||
<div style="font-size:13px;line-height:1.5;margin-top:6px">
|
||
<p style="margin:0 0 6px">bj нужен <b>отдельный логин</b> в базе DEPO. Его создаёт администратор СУБД. Права минимальные:</p>
|
||
<ul style="margin:0 0 6px;padding-left:18px">
|
||
<li><b>чтение</b> учётных таблиц DEPO (лица, счета, остатки, бумаги) — для сверки переводов;</li>
|
||
<li><b>запись только в таблицы обмена</b> <code>FTV_M2M*</code> — их права выдаются на следующем экране, вместе с созданием таблиц.</li>
|
||
</ul>
|
||
<p style="margin:0 0 6px">Вариант 1 — администратор создаёт логин сам с этими правами. Вариант 2 — передайте ему скрипт: в нём подставлены тип СУБД, база, имя логина и пароль из полей выше (если пароль не введён — впишите его в скрипт сами). Скрипт с паролем не храните.</p>
|
||
<a id="wiz-login-dl" href="#">Скачать скрипт создания логина</a> <span class="muted" id="wiz-login-pwnote" style="font-size:12px"></span>
|
||
<pre class="copyable" id="wiz-login-pre" style="max-height:260px;margin-top:6px;font-size:12px">…</pre>
|
||
<p class="muted" style="margin:4px 0 0">После выполнения скрипта впишите логин и пароль в поля выше и нажмите «Проверить и далее».</p>
|
||
</div>
|
||
</details>
|
||
<script>
|
||
(function(){
|
||
var f=document.currentScript.closest('form'), d=document.getElementById('wiz-login-help'), pre=document.getElementById('wiz-login-pre'), a=document.getElementById('wiz-login-dl'), note=document.getElementById('wiz-login-pwnote');
|
||
function val(n){ var e=f.querySelector('[name='+n+']'); return e?e.value:''; }
|
||
function params(){
|
||
var db=f.querySelector('[name=depo_database]');
|
||
var p=new URLSearchParams();
|
||
p.set('type', val('db_type')||'mssql');
|
||
p.set('db', db&&db.value.trim()?db.value.trim():(db?db.placeholder:''));
|
||
p.set('login', val('user').trim()||'bj_user');
|
||
p.set('password', val('password')); // только в теле POST — не в адресе и не в журналах
|
||
return p;
|
||
}
|
||
function upd(){
|
||
note.textContent = val('password') ? '— пароль из поля подставлен в скрипт' : '— пароль не введён: в скрипте заглушка, замените её';
|
||
if(!d.open) return;
|
||
var p=params(); p.set('download','0');
|
||
fetch('/admin/setup/fancy/depo-login/script.sql',{method:'POST',body:p,cache:'no-store'}).then(function(r){return r.text()}).then(function(t){pre.textContent=t}).catch(function(){});
|
||
}
|
||
a.addEventListener('click', function(e){
|
||
e.preventDefault();
|
||
var p=params(), fm=document.createElement('form');
|
||
fm.method='POST'; fm.action='/admin/setup/fancy/depo-login/script.sql'; fm.style.display='none';
|
||
p.forEach(function(v,k){ var i=document.createElement('input'); i.type='hidden'; i.name=k; i.value=v; fm.appendChild(i); });
|
||
document.body.appendChild(fm); fm.submit(); fm.remove();
|
||
});
|
||
d.addEventListener('toggle',upd);
|
||
['db_type','depo_database','user','password'].forEach(function(n){var e=f.querySelector('[name='+n+']'); if(e){ e.addEventListener('change',upd); e.addEventListener('input',function(){ clearTimeout(e._t); e._t=setTimeout(upd,400); }); }});
|
||
upd();
|
||
})();
|
||
</script>
|
||
<input type="hidden" name="encrypt" value="{{if .Settings.Fancy.Encrypt}}{{.Settings.Fancy.Encrypt}}{{else}}disable{{end}}">
|
||
<div style="display:flex;justify-content:space-between;margin-top:6px"><a href="/admin/wizard?step=3&sub=intro" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);text-decoration:none">← Назад</a><button type="submit" class="btn">Проверить и далее →</button></div>
|
||
</form>
|
||
|
||
{{else if eq $sub "tables"}}
|
||
<p>Четыре таблицы обмена <code>FTV_M2M*</code> в базе {{.DepoTables.DB}}: через них bj передаёт поручения и события в DEPO. Данные учёта они не трогают. Кто их создаёт?</p>
|
||
<form method="post" action="/admin/setup/wizard-depo/tables" style="display:grid;gap:10px;max-width:680px" id="wiz-tables">
|
||
<label style="display:flex;gap:8px;align-items:flex-start"><input type="radio" name="depo_tables_mode" value="auto" {{if ne .Settings.Fancy.DepoTablesMode "script"}}checked{{end}} onchange="wizTbl()"> <span><b>bj создаёт сам</b> <span class="muted" style="font-size:13px">— по «Далее». Если у логина {{.DepoTables.User}} нет прав на создание таблиц, введите разово логин администратора SQL (не сохраняется).</span></span></label>
|
||
<label style="display:flex;gap:8px;align-items:flex-start"><input type="radio" name="depo_tables_mode" value="script" {{if eq .Settings.Fancy.DepoTablesMode "script"}}checked{{end}} onchange="wizTbl()"> <span><b>Создаёт администратор по скрипту</b> <span class="muted" style="font-size:13px">— передайте ему скрипт, после выполнения нажмите «Проверить и далее».</span></span></label>
|
||
<div id="wiz-tbl-auto" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:8px">
|
||
<input type="text" name="admin_user" autocomplete="off" placeholder="логин администратора (необязательно)" style="padding:8px;width:100%;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px">
|
||
<input type="password" name="admin_password" autocomplete="new-password" placeholder="пароль" style="padding:8px;width:100%;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px">
|
||
</div>
|
||
<div id="wiz-tbl-script" style="display:none"><a href="/admin/setup/fancy/depo-tables/script.sql">Скачать скрипт {{.DepoTables.ScriptFile}}</a>
|
||
<details style="margin-top:6px"><summary style="cursor:pointer;font-size:13px">Посмотреть скрипт</summary><pre class="copyable" style="max-height:300px;overflow:auto;font-size:12px">{{.DepoTables.Script}}</pre></details>
|
||
<input type="hidden" name="action" value="" id="wiz-tbl-action"></div>
|
||
<div style="display:flex;justify-content:space-between;margin-top:6px"><a href="/admin/wizard?step=3&sub=conn" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);text-decoration:none">← Назад</a><button type="submit" class="btn" id="wiz-tbl-btn">Создать и далее →</button></div>
|
||
</form>
|
||
<script>function wizTbl(){var f=document.getElementById('wiz-tables'),sc=f.depo_tables_mode.value==='script';
|
||
document.getElementById('wiz-tbl-auto').style.display=sc?'none':'grid';document.getElementById('wiz-tbl-script').style.display=sc?'block':'none';
|
||
document.getElementById('wiz-tbl-action').value=sc?'check':'';document.getElementById('wiz-tbl-btn').textContent=sc?'Проверить и далее →':'Создать и далее →';}wizTbl();</script>
|
||
|
||
{{else if eq $sub "job"}}
|
||
<p>Процедура <code>{{.DepoJob.Proc}}</code> в базе DEPO каждые {{.DepoJob.Every}} с превращает записи bj в документы и операции учёта. Кто её запускает?</p>
|
||
{{if .DepoEdition}}<p class="muted" style="font-size:13px">Ваш сервер: {{.DepoEdition}} — {{if .DepoAgent}}есть SQL Server Agent, рекомендуем первый вариант.{{else}}без SQL Server Agent, подходит вариант «запускает bj».{{end}}</p>{{end}}
|
||
{{$mode := .Settings.Fancy.DepoJobMode}}{{if not $mode}}{{$mode = .DepoJobDef}}{{end}}{{if not $mode}}{{$mode = "sql-auto"}}{{end}}
|
||
<form method="post" action="/admin/setup/wizard-depo/job" style="display:grid;gap:10px;max-width:680px" id="wiz-job">
|
||
{{if not .DepoTables.Postgres}}<label style="display:flex;gap:8px;align-items:flex-start"><input type="radio" name="depo_job_mode" value="sql-auto" {{if eq $mode "sql-auto"}}checked{{end}} onchange="wizJob()"> <span><b>Джоба SQL Server Agent, создаёт bj</b> <span class="muted" style="font-size:13px">— рекомендуется: работает независимо от bj. По «Далее» bj создаст джобу; нужен разово логин администратора SQL (sa) — не сохраняется.</span></span></label>{{end}}
|
||
<label style="display:flex;gap:8px;align-items:flex-start"><input type="radio" name="depo_job_mode" value="bj" {{if eq $mode "bj"}}checked{{end}} onchange="wizJob()"> <span><b>Запускает сам bj</b> <span class="muted" style="font-size:13px">— для SQL Server Express и PostgreSQL. Администратору SQL нужно выдать одно право (скрипт ниже).</span></span></label>
|
||
{{if not .DepoTables.Postgres}}<label style="display:flex;gap:8px;align-items:flex-start"><input type="radio" name="depo_job_mode" value="sql-manual" {{if eq $mode "sql-manual"}}checked{{end}} onchange="wizJob()"> <span><b>Джоба SQL Server Agent, создаёт администратор</b> <span class="muted" style="font-size:13px">— передайте ему скрипт.</span></span></label>{{end}}
|
||
<div id="wiz-job-auto" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:8px">
|
||
<input type="text" name="admin_user" autocomplete="off" placeholder="логин администратора SQL, например sa" style="padding:8px;width:100%;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px">
|
||
<input type="password" name="admin_password" autocomplete="new-password" placeholder="пароль" style="padding:8px;width:100%;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px">
|
||
</div>
|
||
<p id="wiz-job-auto-hint" class="muted" style="font-size:12.5px;margin:-4px 0 0">Нет логина администратора SQL или его не хотят давать — выберите ниже «Джоба SQL Server Agent, создаёт администратор по скрипту»: тот же результат, скрипт выполняет администратор.</p>
|
||
<div id="wiz-job-bj" style="display:none"><p style="margin:0 0 4px;font-size:13px"><b>Администратору SQL — выполнить один раз:</b></p><pre class="copyable" style="font-size:12px">{{.DepoJob.GrantScript}}</pre></div>
|
||
<div id="wiz-job-manual" style="display:none"><p style="margin:0 0 4px;font-size:13px"><b>Скрипт для администратора SQL</b> — выполнить один раз под sysadmin, затем нажать «Проверить и далее». Повторный запуск безопасен.</p>
|
||
<a href="/admin/setup/fancy/depo-job/script.sql">Скачать скрипт bj-depo-job-mssql.sql</a>
|
||
<pre class="copyable" style="font-size:12px;max-height:260px">{{.DepoJob.AdminScript}}</pre></div>
|
||
<input type="hidden" name="action" value="" id="wiz-job-action">
|
||
<div style="display:flex;justify-content:space-between;margin-top:6px"><a href="/admin/wizard?step=3&sub=tables" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8);text-decoration:none">← Назад</a><button type="submit" class="btn" id="wiz-job-btn">Создать, проверить и далее →</button></div>
|
||
<p class="muted" style="font-size:12px;margin:0">Проверка: джоба есть, включена, есть расписание и она запускалась за последнюю минуту. Работают ли процедуры обработки — отдельная проверка на экране «Готово».</p>
|
||
<p style="font-size:12.5px;margin:0"><a href="/admin/wizard?step=3&sub=done">Дальше без проверки →</a> <span class="muted">— если обработку ещё настраивает администратор (например, ставит процедуры). Вернуться и проверить можно с экрана «Готово» кнопкой «Перепроверить всё».</span></p>
|
||
</form>
|
||
<script>function wizJob(){var f=document.getElementById('wiz-job'),m=f.depo_job_mode.value;
|
||
document.getElementById('wiz-job-auto').style.display=m==='sql-auto'?'grid':'none';document.getElementById('wiz-job-auto-hint').style.display=m==='sql-auto'?'block':'none';document.getElementById('wiz-job-bj').style.display=m==='bj'?'block':'none';document.getElementById('wiz-job-manual').style.display=m==='sql-manual'?'block':'none';
|
||
document.getElementById('wiz-job-action').value=m==='sql-auto'?'':'check';document.getElementById('wiz-job-btn').textContent=m==='sql-auto'?'Создать, проверить и далее →':'Проверить и далее →';}wizJob();</script>
|
||
|
||
{{else if eq $sub "done"}}
|
||
<table style="margin:6px 0 10px">
|
||
<tr><td>Подключение к базе DEPO</td><td>{{if .Settings.Fancy.Server}}<span style="color:var(--ok)">✓ {{.Settings.Fancy.Server}}, база {{.DepoTables.DB}}</span>{{else}}<span style="color:var(--err)">не настроено</span>{{end}}</td></tr>
|
||
<tr><td>Таблицы обмена FTV_M2M</td><td>{{if .Settings.Fancy.DepoTablesCheckOK}}<span style="color:var(--ok)">✓ на месте</span>{{else}}<span style="color:var(--err)">✗ {{.Settings.Fancy.DepoTablesCheck}}</span>{{end}}</td></tr>
|
||
<tr><td>Запуск обработки (джоба)</td><td>{{if .Settings.Fancy.DepoJobCheckOK}}<span style="color:var(--ok)">✓ {{.Settings.Fancy.DepoJobCheck}}</span>{{else}}<span style="color:var(--err)">✗ {{.Settings.Fancy.DepoJobCheck}}</span>{{end}}</td></tr>
|
||
<tr><td>Обработка событий (процедуры)</td><td>{{if .Settings.Fancy.DepoProcCheckOK}}<span style="color:var(--ok)">✓ {{.Settings.Fancy.DepoProcCheck}}</span>{{else if .Settings.Fancy.DepoProcCheck}}<span style="color:var(--warn)">⚠ {{.Settings.Fancy.DepoProcCheck}}</span>{{else}}<span class="muted">не проверялась — «Перепроверить всё»</span>{{end}}</td></tr>
|
||
</table>
|
||
{{if .Settings.Fancy.Server}}{{template "accPrefix" .}}{{end}}
|
||
{{if .Done.Depo}}<p>Учёт DEPO подключён и проверен. Дальше — «Организация»: bj предложит реквизиты вашей организации из DEPO.</p>{{else}}<p class="muted" style="font-size:13px">Не всё проверено — можно продолжить настройку (шаг 4 и дальше) и вернуться сюда позже. Пока обработка не работает, записи bj ждут в таблицах обмена со статусом TODO и будут обработаны, когда она заработает.</p>{{end}}
|
||
<form method="post" action="/admin/setup/wizard-depo/recheck" style="margin:0"><button type="submit" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8)">Перепроверить всё</button></form>
|
||
{{end}}
|
||
{{end}}
|
||
{{template "wizNav" .}}
|
||
</div>
|
||
{{end}}
|
||
{{/* ============= ШАГ 2: База bj (PostgreSQL) ============= */}}
|
||
{{if eq .Step 2}}
|
||
<div class="card">
|
||
<h2><span class="dot {{if .Done.Postgres}}ok{{else}}err{{end}}"></span>База данных 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> — если вы ставили bj с образа или командой установки, этот шаг уже выполнен. Если база bj на отдельном сервере PostgreSQL — введите строку подключения ниже{{if devOn}}, или поднимите локальную базу автоматически (режим разработки){{end}}.
|
||
</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>
|
||
|
||
{{template "wizNav" .}}
|
||
{{if and devOn (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}}
|
||
|
||
{{/* ============= ШАГ 4: Наша организация ============= */}}
|
||
{{if eq .Step 4}}
|
||
<div class="card">
|
||
<h2><span class="dot {{if .Done.Org}}ok{{else}}err{{end}}"></span>Наша организация</h2>
|
||
<div class="help-block"><strong>Что это.</strong> Депозитарий — участник MOEX МОСТ, от имени которого bj отправляет и принимает переводы: код участника, ИНН и наименование попадают в пакеты НРД{{if .DepoLicensed}} и в поручения QUIK{{end}}.<br>
|
||
<strong>Откуда взять.</strong> {{if .DepoLicensed}}Если подключена база DEPO (раздел «Учёт DEPO»), bj сам определяет реквизиты по учёту — проверьте их и сохраните. Иначе выберите{{else}}Выберите{{end}} себя из справочника участников НРД или введите вручную.</div>
|
||
{{if .Suggest}}{{if .Suggest.OrgName}}{{if not .Done.Org}}<p style="margin:8px 0;padding:8px 12px;border-radius:8px;background:var(--accent-weak)">✓ <strong>Определено из DEPO</strong>: {{.Suggest.OrgName}}, ИНН {{.Suggest.OrgINN}}, код {{.Suggest.OrgCode}}{{if .Suggest.AccountID}}, счёт НРД {{.Suggest.AccountID}}{{end}}. Поля ниже заполнены — проверьте и нажмите «Сохранить».</p>{{end}}{{end}}{{end}}
|
||
{{with .Fancy}}{{template "orgSection" .}}{{end}}
|
||
<div class="card" id="nsd-req" style="margin-top:12px">
|
||
<h2><span class="dot {{if and .Settings.NSD.DeponentCode .Settings.NSD.AccountID .Settings.NSD.SectionID}}ok{{else}}err{{end}}"></span>Депозитарные реквизиты в НРД</h2>
|
||
<p class="muted" style="margin:0 0 8px">Где в НРД учитываются бумаги вашей организации: место хранения указывается в каждом пакете перевода. Берутся из договора с НРД{{if .Settings.Fancy.Server}} — или кнопкой «Определить из DEPO»{{end}}.</p>
|
||
{{if .Suggest}}{{if .Suggest.AccountID}}<div class="help-block" style="margin:0 0 8px"><strong>Найдено в DEPO:</strong> счёт {{.Suggest.AccountID}}, код {{.Suggest.DeponentCode}}, раздел {{.Suggest.SectionID}}.{{range .Suggest.Notes}}<br><span class="muted">⚠ {{.}}</span>{{end}}</div>{{end}}{{end}}
|
||
<form method="post" action="/admin/setup/nsd-requisites" style="display:grid;gap:10px;max-width:680px">
|
||
<input type="hidden" name="return_to" value="/admin/wizard?step=4">
|
||
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px">
|
||
<label style="margin:0">Депозитарный код<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;width:100%;box-sizing:border-box;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px;margin-top:4px"></label>
|
||
<label style="margin:0">Депозитарный счёт<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;width:100%;box-sizing:border-box;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px;margin-top:4px"></label>
|
||
<label style="margin:0">Раздел счёта<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;width:100%;box-sizing:border-box;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px;margin-top:4px"></label>
|
||
</div>
|
||
<div style="display:flex;gap:8px;flex-wrap:wrap"><button type="submit" class="btn">Сохранить</button>{{if .Settings.Fancy.Server}}<button type="submit" class="btn" formaction="/admin/setup/fancy/detect" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8)">Определить из DEPO</button>{{end}}</div>
|
||
</form>
|
||
</div>
|
||
<div class="card" style="margin-top:12px">
|
||
<h3 style="margin:0 0 6px">Логотип организации (необязательно)</h3>
|
||
<p class="muted" style="margin:0 0 10px;font-size:13px">Показывается справа в шапке отчётов для регулятора. Можно загрузить сейчас или позже в «Настройка → Оформление».</p>
|
||
{{if .Settings.Branding.LogoDataURI}}<p style="margin:0 0 8px"><img src="{{.Settings.Branding.LogoDataURI}}" alt="логотип" style="height:36px;background:#fff;padding:4px 8px;border-radius:6px;border:1px solid var(--border)"></p>{{end}}
|
||
<form method="post" action="/admin/setup/logo" enctype="multipart/form-data" style="display:flex;gap:10px;flex-wrap:wrap;align-items:center">
|
||
<input type="hidden" name="return_to" value="/admin/wizard?step=4">
|
||
<input type="file" name="logo" accept="image/png,image/jpeg,image/gif,image/webp,image/svg+xml,.png,.jpg,.jpeg,.gif,.webp,.svg" required>
|
||
<button type="submit" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8)">{{if .Settings.Branding.LogoDataURI}}Заменить логотип{{else}}Загрузить логотип{{end}}</button>
|
||
</form>
|
||
</div>
|
||
{{template "wizNav" .}}
|
||
</div>
|
||
{{end}}
|
||
|
||
{{/* ============= ШАГ 5: QUIK МНП ============= */}}
|
||
{{if eq .Step 5}}
|
||
<div class="card">
|
||
<h2><span class="dot {{if .Done.Quik}}ok{{else}}err{{end}}"></span>QUIK — модуль неторговых поручений</h2>
|
||
<div class="help-block"><strong>Что это.</strong> Модуль неторговых поручений QUIK (МНП) — продукт компании ARQA Technologies, разработчика QUIK. Через него bj блокирует бумаги клиента в QUIK на время перевода, снимает блокировку при отказе и после исполнения проводит поручение.<br>
|
||
<strong>Если МНП у вас ещё нет.</strong> Обратитесь в ARQA Technologies (<a href="https://arqatech.com" target="_blank" rel="noopener">arqatech.com</a>, служба поддержки QUIK) и попросите подключить модуль неторговых поручений для работы с внешней системой. После подключения ARQA выдаст адрес сервера МНП и учётные данные — их и нужно ввести ниже.<br>
|
||
<strong>Если МНП уже есть.</strong> Адрес сервера и учётные данные — у вашего администратора QUIK.</div>
|
||
{{if .DepoLicensed}}{{template "letterCard" (letter "arqa")}}{{end}}
|
||
{{if not .DepoLicensed}}<p class="muted">Модуль неторговых поручений не входит в текущую лицензию — шаг пропускается.</p>
|
||
{{else}}{{with .Fancy}}{{template "quikSection" .}}{{end}}
|
||
{{if .Settings.Quik.Server}}{{template "mnpCheck" .}}{{end}}
|
||
{{if .Settings.Quik.M2MMissing}}<p style="color:var(--err);margin:8px 0">✗ Подключение есть, но под этим логином не видно объектов M2M: {{.Settings.Quik.M2MMissing}}. Проверьте имя базы МНП и права логина (роль InstrEu) и нажмите «Проверить» ещё раз.</p>{{end}}
|
||
<div class="help-block" style="margin-top:12px"><strong>Справочник депозитариев-получателей.</strong> Чтобы QUIK принимал поручения M2M, в МНП должен быть справочник депозитариев-получателей (<code>M2MDepoRecipients</code>): bj заполняет его сам по справочнику участников НРД. <b>Модуль МНП читает справочник только при запуске</b> — после первого заполнения попросите администратора QUIK перезапустить модуль неторговых поручений, иначе QUIK ответит «Неизвестный депозитарий получателя».<br><span class="muted">{{quikRecipientsInfo}}</span></div>{{end}}
|
||
{{template "wizNav" .}}
|
||
</div>
|
||
{{end}}
|
||
|
||
{{/* ============= ШАГ 6: Валидата ============= */}}
|
||
{{if eq .Step 6}}
|
||
<div class="card">
|
||
<h2><span class="dot {{if .Done.Crypto}}ok{{else}}err{{end}}"></span>СКЗИ «Валидата Клиент L»</h2>
|
||
<p>СКЗИ «Валидата Клиент L» проверяет подписи НРД на квитанциях и сообщениях и нужно для работы с ключами и сертификатами (раздел «Ключ»).</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}} — сейчас она не запущена: служба ставится обновлением с сервера лицензий (раздел «Лицензия», кнопка «Обновить сейчас»), а работает с ключом, установленным в разделе «Ключ»{{end}}.</p>
|
||
{{else}}
|
||
<p style="color:var(--err)">Валидата на этом сервере не найдена.</p>
|
||
<div class="help-block">
|
||
<strong>Как поставить.</strong> С образа Bridge&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}}
|
||
|
||
{{template "wizNav" .}}
|
||
</div>
|
||
{{end}}
|
||
|
||
{{/* ============= ШАГ 7: Шлюз НРД (до ключа: ключ ставится в профиль ИШ) ============= */}}
|
||
{{if eq .Step 7}}
|
||
<div class="card">
|
||
<h2><span class="dot {{if .Done.NSD}}ok{{else}}err{{end}}"></span>Интеграционный шлюз НРД (ИШ)</h2>
|
||
<div class="help-block">
|
||
<strong>Что это.</strong> Интеграционный шлюз — программа НРД на этом же сервере: через неё bj отправляет пакеты M2M в НРД и получает ответы. bj работает со шлюзом по его адресу на сервере (обычно <code>http://localhost:8090</code>).<br>
|
||
<strong>К кому обратиться.</strong> Дистрибутив ИШ выпускает НРД (<a href="https://www.nsd.ru/workflow/system/programs/" target="_blank" rel="noopener">nsd.ru/workflow/system/programs</a>). Доступ к контурам — сначала тестовому TEST3, затем боевому — НРД открывает по заявке: обратитесь к своему менеджеру НРД или в поддержку электронного документооборота.
|
||
</div>
|
||
{{template "letterCard" (letter "nsd-test")}}
|
||
{{template "letterCard" (letter "nsd-prod")}}
|
||
{{template "igateInstall" .}}
|
||
<details style="margin-top:8px"><summary style="cursor:pointer;color:var(--accent);font-size:13px">Дополнительно: адрес шлюза (обычно не нужно)</summary>
|
||
<form method="post" action="/admin/setup/nsd" style="margin-top:10px;display:grid;gap:10px;max-width:680px">
|
||
<input type="hidden" name="return_to" value="/admin/wizard?step=7">
|
||
{{if .Settings.NSD.IGWv904}}<input type="hidden" name="igw_v904" value="on">{{end}}
|
||
<input type="hidden" name="keep_requisites" value="1">
|
||
<input type="hidden" name="profile" value="{{if .Settings.NSD.Profile}}{{.Settings.NSD.Profile}}{{else}}test3-gost{{end}}">
|
||
<label style="margin:0">Адрес шлюза на этом сервере
|
||
<input type="text" name="igw_base_url" value="{{if .Settings.NSD.IGWBaseURL}}{{.Settings.NSD.IGWBaseURL}}{{else}}http://localhost:8090{{end}}" placeholder="http://localhost:8090" style="padding:8px;width:100%;box-sizing:border-box;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px;margin-top:4px"></label>
|
||
<p id="igw-onyx" style="display:none;color:var(--err);font-size:13px;margin:0">Это адрес веб-сервиса НРД (ONYX), а не шлюза: bj работает через ИШ на этом сервере. Укажите адрес шлюза — обычно <a href="#" id="igw-def">http://localhost:8090</a>.</p>
|
||
<script>(function(){var u=document.querySelector('[name=igw_base_url]'),w=document.getElementById('igw-onyx');
|
||
function chk(){w.style.display=/onyx|nsd\.ru/i.test(u.value)?'block':'none'} u.addEventListener('input',chk); chk();
|
||
document.getElementById('igw-def').onclick=function(e){e.preventDefault();u.value='http://localhost:8090';chk()};})();</script>
|
||
<div><button type="submit" class="btn">Сохранить и проверить</button></div>
|
||
</form>
|
||
</details>
|
||
<p class="muted" style="font-size:13px;margin-top:12px">Депозитарные реквизиты в НРД: {{if and .Settings.NSD.DeponentCode .Settings.NSD.AccountID .Settings.NSD.SectionID}}код <code>{{.Settings.NSD.DeponentCode}}</code>, счёт <code>{{.Settings.NSD.AccountID}}</code>, раздел <code>{{.Settings.NSD.SectionID}}</code>{{else}}<span style="color:var(--err)">не заданы</span>{{end}} — в разделе <a href="/admin/wizard?step=4#nsd-req">«Организация»</a>.</p>
|
||
{{template "wizNav" .}}
|
||
</div>
|
||
{{end}}
|
||
|
||
{{/* ============= ШАГ 8: Ключ и сертификаты (после шлюза: ключ ставится в профиль ИШ) ============= */}}
|
||
{{if eq .Step 8}}
|
||
<div class="card">
|
||
<h2><span class="dot {{if .Done.Certs}}ok{{else}}err{{end}}"></span>Ключ и сертификаты</h2>
|
||
<p>Здесь две части: ключ и сертификат вашей организации (ставите вы) и сертификаты НРД и удостоверяющих центров (bj ведёт их сам).</p>
|
||
{{if not .Done.NSD}}<p style="padding:8px 12px;border-radius:8px;background:var(--accent-weak);margin:8px 0">Ключ устанавливается в профиль интеграционного шлюза — сначала установите шлюз в разделе <a href="/admin/wizard?step=7">«Шлюз НРД»</a>.</p>{{end}}
|
||
|
||
<h3 style="margin-top:14px">1. Ключ и сертификат организации</h3>
|
||
<div class="help-block"><strong>Что это.</strong> Ключ электронной подписи вашей организации для обмена с НРД и его сертификат.<br>
|
||
<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">Первая сверка пройдёт сразу после установки ключа.</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"}}{{if .HasPrivateKey}}{{$d := certDaysLeft .NotAfter}}{{if lt $d 0}} <span style="color:var(--err)">истёк</span>{{else if le $d 30}} <span style="color:var(--warn)">осталось {{$d}} дн.</span>{{end}}{{end}}</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}}
|
||
|
||
{{template "wizNav" .}}
|
||
</div>
|
||
{{end}}
|
||
|
||
{{/* ============= ШАГ 12: Время и сеть (глава «Сервер») ============= */}}
|
||
{{if eq .Step 12}}
|
||
<div class="card">
|
||
<h2><span class="dot {{if .Done.Server}}ok{{else}}warn{{end}}"></span>Время и сеть сервера</h2>
|
||
{{if .AutoDone}}<div class="help-block"><strong>Сделано автоматически.</strong>{{range .AutoDone}}<br>✓ {{.}}{{end}}</div>{{end}}
|
||
<h3 style="margin:14px 0 4px">Часовой пояс</h3>
|
||
<p class="muted" style="margin:0 0 6px;font-size:13px">По нему — время в журналах, уведомлениях и сроках поручений. Время НРД — московское; если сервер в другом поясе, выберите свой.</p>
|
||
{{$tz := serverTimeZone}}
|
||
<form method="post" action="/admin/setup/timezone" style="display:flex;gap:8px;flex-wrap:wrap;align-items:end">
|
||
<input type="hidden" name="return_to" value="/admin/wizard?step=12">
|
||
<select name="zone" style="padding:8px;min-width:260px;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px">{{range timeZones}}<option value="{{.Zone}}" {{if eq .Zone $tz}}selected{{end}}>{{.Title}}</option>{{end}}</select>
|
||
<button type="submit" class="btn">Применить</button>
|
||
<span class="muted" style="font-size:12.5px">сейчас: {{$tz}}</span>
|
||
</form>
|
||
<h3 style="margin:16px 0 4px">Сервер времени</h3>
|
||
<p style="margin:0 0 6px;font-size:13px">{{if .Done.Server}}<span style="color:var(--ok)">✓ время синхронизируется</span>{{else}}<span style="color:var(--warn)">⚠ время не синхронизируется — укажите сервер времени вашей сети (обычно контроллер домена или маршрутизатор)</span>{{end}}</p>
|
||
<form method="post" action="/admin/setup/time" style="display:flex;gap:8px;flex-wrap:wrap;align-items:end">
|
||
<input type="hidden" name="return_to" value="/admin/wizard?step=12">
|
||
<input name="servers" value="{{.Settings.Time.Servers}}" placeholder="10.10.10.1 ntp.example.ru" style="padding:8px;min-width:260px;background:var(--bg);border:1px solid var(--border);color:var(--text);border-radius:4px">
|
||
<button type="submit" class="btn" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8)">Применить</button>
|
||
</form>
|
||
<h3 style="margin:16px 0 4px">Адрес и HTTPS</h3>
|
||
<p style="margin:0;font-size:13px">Сервер: <code>{{serverAddrs}}</code>. Вход по HTTPS: <a href="https://{{serverIP}}:8443/admin/">https://{{serverIP}}:8443/</a>.</p>
|
||
<p class="muted" style="font-size:12.5px;margin:4px 0 0">Чтобы адрес не менялся, задайте постоянный — на консоли сервера: <code>sudo bj-network</code>. Перенаправление на HTTPS и сертификат организации — в «Настройка → Сервер».</p>
|
||
{{template "wizNav" .}}
|
||
</div>
|
||
{{end}}
|
||
|
||
{{/* ============= ШАГ 11: Модули по лицензии (глава «Запуск») ============= */}}
|
||
{{if eq .Step 11}}
|
||
<div class="card">
|
||
<h2><span class="dot {{if .Done.Modules}}ok{{else}}warn{{end}}"></span>Модули по лицензии</h2>
|
||
<p class="muted" style="margin:0 0 8px">Здесь только модули, которые входят в вашу лицензию. Каждый настраивается в своём разделе — кнопка «Настроить» откроет его, а вернуться в мастер можно ссылкой вверху раздела.</p>
|
||
{{range .Modules}}
|
||
<div style="border:1px solid var(--border);border-radius:8px;padding:10px 12px;margin:8px 0">
|
||
<div style="display:flex;justify-content:space-between;gap:8px;align-items:center;flex-wrap:wrap">
|
||
<b><span class="dot {{if .Ready}}ok{{else}}warn{{end}}"></span>{{.Title}}</b>
|
||
<a href="{{.Link}}" class="btn" style="text-decoration:none;padding:5px 12px">{{if .Ready}}Открыть{{else}}Настроить{{end}} →</a>
|
||
</div>
|
||
<p class="muted" style="font-size:13px;margin:6px 0 2px">{{.What}}</p>
|
||
<p style="font-size:13px;margin:0;color:var({{if .Ready}}--ok{{else}}--warn{{end}})">{{if .Ready}}✓{{else}}⚠{{end}} {{.Status}}</p>
|
||
</div>
|
||
{{end}}
|
||
{{template "wizNav" .}}
|
||
</div>
|
||
{{end}}
|
||
|
||
{{/* ============= ШАГ 9: Оповещения и резервные копии ============= */}}
|
||
{{if eq .Step 9}}
|
||
<div class="card">
|
||
<h2><span class="dot {{if .Done.Alerts}}ok{{else}}warn{{end}}"></span>Оповещения и резервные копии</h2>
|
||
<div class="help-block"><strong>Зачем.</strong> bj сам следит за обменом: НРД не отвечает, перевод не доставлен{{if .DepoLicensed}}, ошибка обработки в DEPO{{end}}, истекает сертификат ключа. Чтобы об этом узнали сразу, а не открыв bj, — укажите почту. Резервные копии позволят восстановить сервер целиком (настройки, заявки, шлюз НРД) после сбоя или переустановки.<br>
|
||
<strong>Можно позже.</strong> Шаг необязательный — обмен работает и без него, но мы настоятельно советуем настроить оба пункта.</div>
|
||
<style>.wiz9 label:not(:has(input[type=checkbox])){display:block;margin:0}.wiz9 label input:not([type=checkbox]),.wiz9 label select,.wiz9 .grid input,.wiz9 .grid select{width:100%;box-sizing:border-box;margin-top:4px}</style>
|
||
<div class="wiz9">
|
||
{{with .Fancy}}
|
||
<h3 style="margin:16px 0 6px">Почта для оповещений</h3>
|
||
{{template "smtpCard" .}}
|
||
{{template "notifyGroupsCard" .}}
|
||
<h3 style="margin:16px 0 6px">Резервные копии</h3>
|
||
{{template "backupCard" .}}
|
||
{{end}}
|
||
</div>
|
||
{{template "wizNav" .}}
|
||
</div>
|
||
{{end}}
|
||
|
||
{{/* ============= ШАГ 10: Проверка готовности (в режиме разработки — ещё тестовая заявка) ============= */}}
|
||
{{if eq .Step 10}}
|
||
<div class="card">
|
||
{{if devOn}}
|
||
<h2><span class="dot {{if .Done.TestRun}}ok{{else}}err{{end}}"></span>Тестовая заявка</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}}
|
||
{{end}}
|
||
{{if not devOn}}<h2><span class="dot" id="rd-dot"></span>Готовность к работе</h2>{{else}}<h3 style="margin-top:18px">Готовность к работе</h3>{{end}}
|
||
<p class="muted" style="margin:4px 0 10px">bj проверяет всё, что нужно для обмена с НРД, прямо сейчас. Красное — обмен работать не будет, жёлтое — работать будет, но лучше исправить. У каждой строки — ссылка туда, где это исправляется.</p>
|
||
<div id="rd-sum" style="font-weight:600;margin:6px 0">Проверяю…</div>
|
||
<table id="rd-tab" style="width:100%"><tbody></tbody></table>
|
||
<p style="margin-top:8px"><button type="button" class="btn" id="rd-again" style="background:var(--card);color:var(--text);border:1px solid var(--border-strong,#c9cdd8)">Проверить ещё раз</button> <span class="muted" id="rd-at" style="font-size:12px"></span></p>
|
||
<script>
|
||
(function(){
|
||
var tb=document.querySelector('#rd-tab tbody'), sum=document.getElementById('rd-sum'), dot=document.getElementById('rd-dot');
|
||
function esc(s){return String(s).replace(/[&<>"]/g,function(c){return{'&':'&','<':'<','>':'>','"':'"'}[c]})}
|
||
var ic={ok:'<span style="color:var(--ok)">✓</span>',warn:'<span style="color:var(--warn)">⚠</span>',err:'<span style="color:var(--err)">✗</span>',skip:'<span class="muted">—</span>'};
|
||
function pl(n,one,few,many){var m=n%100,d=n%10;return (m>=11&&m<=14)?many:d===1?one:(d>=2&&d<=4)?few:many}
|
||
function load(){ sum.textContent='Проверяю…'; tb.innerHTML='';
|
||
fetch('/admin/setup/readiness',{cache:'no-store'}).then(function(r){return r.json()}).then(function(d){
|
||
tb.innerHTML=d.checks.map(function(c){return '<tr><td style="width:24px;vertical-align:top">'+ic[c.state]+'</td><td style="vertical-align:top;white-space:nowrap;padding-right:10px"><b>'+esc(c.title)+'</b></td><td style="vertical-align:top">'+esc(c.detail)+(c.state!=='ok'&&c.link?' <a href="'+esc(c.link)+'">Исправить →</a>':'')+'</td></tr>'}).join('');
|
||
var red=d.checks.filter(function(c){return c.state==='err'}).length, yel=d.checks.filter(function(c){return c.state==='warn'}).length;
|
||
sum.innerHTML=d.ready?(yel?'<span style="color:var(--ok)">✓ Обмен с НРД готов к работе</span> <span class="muted" style="font-weight:400">— желательно исправить ещё '+yel+' '+pl(yel,'пункт','пункта','пунктов')+' (отмечены ⚠)</span>':'<span style="color:var(--ok)">✓ Всё готово к работе</span>'):'<span style="color:var(--err)">✗ Пока не готово: осталось '+red+' '+pl(red,'обязательный пункт','обязательных пункта','обязательных пунктов')+' (отмечены ✗)</span>';
|
||
if(dot) dot.className='dot '+(d.ready?(yel?'warn':'ok'):'err');
|
||
document.getElementById('rd-at').textContent='проверено '+d.at;
|
||
}).catch(function(){ sum.textContent='Проверка не выполнилась — обновите страницу.'; }); }
|
||
document.getElementById('rd-again').onclick=load; load();
|
||
})();
|
||
</script>
|
||
</div>
|
||
{{template "chainTest" .}}
|
||
<div class="card">
|
||
<h2>Письма для подключения</h2>
|
||
<p class="muted" style="margin:0 0 6px">Готовые письма с вашими реквизитами — тем, без кого подключение не закончить.</p>
|
||
{{template "letterCard" (letter "nsd-test")}}
|
||
{{template "letterCard" (letter "nsd-prod")}}
|
||
{{if .DepoLicensed}}{{template "letterCard" (letter "arqa")}}{{template "letterCard" (letter "dba")}}{{end}}
|
||
|
||
|
||
|
||
{{template "wizNav" .}}
|
||
</div>
|
||
{{end}}
|
||
|
||
</div>
|
||
{{end}}
|