ИШ ставится при подготовке; на шаге «Шлюз НРД» — версия и «Установить заново»; ссылка на НРД вместо писем
- «Подготовка модулей»: ИШ нет на сервере — bj ставит его из дистрибутива на сервере (образ или обновление bj — версия, проверенная ZETIT), ход — в списке. - Шаг «Шлюз НРД»: установлен и работает — версия; не стоит или не работает — одна кнопка «Установить ИШ» / «Установить ИШ заново» (систему переставлять не нужно). «Скачать с сайта НРД» и «Установить из файла» — только в режиме разработки: версию ИШ меняем после своей проверки, вместе с обновлением bj. - Каналы TEST3 и боевого: до установки ИШ — «канал создаётся после установки ИШ», после — «Создать канал». - «Письма в НРД о доступе к контурам» убраны: НРД подключает заявкой на сайте — ссылка на «MOEX МОСТ для M2M», раздел «Как подключиться». Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
This commit is contained in:
@@ -329,6 +329,37 @@ func refreshCertStore(rc *RuntimeConfig) {
|
||||
}
|
||||
|
||||
// igateBundledVersion — версия ИШ, лежащего на сервере с образа (/var/lib/bj/igate-dist; "" — нет).
|
||||
// igateInstallState — ход установки ИШ (файл пишет установщик bj-igate-admin).
|
||||
type igateInstallState struct {
|
||||
Stage, Msg string
|
||||
Busy bool // установка идёт (не done/error и файл обновлялся недавно)
|
||||
}
|
||||
|
||||
// igateInstallStatusPath — файл хода установки; var — для тестов.
|
||||
var igateInstallStatusPath = "/var/lib/bj/igate-install-status.json"
|
||||
|
||||
func igateInstallStatus() igateInstallState {
|
||||
p := igateInstallStatusPath
|
||||
var st igateInstallState
|
||||
b, err := os.ReadFile(p)
|
||||
if err != nil {
|
||||
return st
|
||||
}
|
||||
var raw struct{ Stage, Msg string }
|
||||
if json.Unmarshal(b, &raw) != nil {
|
||||
return st
|
||||
}
|
||||
st.Stage, st.Msg = raw.Stage, raw.Msg
|
||||
if fi, err := os.Stat(p); err == nil && st.Stage != "done" && st.Stage != "error" {
|
||||
// установщик обновляет файл на каждом этапе; молчит 20 минут — установка прервалась
|
||||
st.Busy = time.Since(fi.ModTime()) < 20*time.Minute
|
||||
if !st.Busy {
|
||||
st.Stage, st.Msg = "error", "установка прервалась ("+st.Msg+")"
|
||||
}
|
||||
}
|
||||
return st
|
||||
}
|
||||
|
||||
func igateBundledVersion() string {
|
||||
m, _ := filepath.Glob("/var/lib/bj/igate-dist/igate-*-amd64.zip")
|
||||
best := ""
|
||||
|
||||
@@ -314,8 +314,9 @@ func TestPrepareScreen(t *testing.T) {
|
||||
}
|
||||
}
|
||||
rc, _ := NewRuntimeConfig(t.TempDir() + "/setup.json")
|
||||
if items := prepareItems(rc, false, false, ""); len(items) != 2 || items[0].Title != "Обновление bj" || items[0].State != "wait" || items[1].State != "ok" {
|
||||
t.Fatalf("без лицензии — обновление (ещё не проверялось) и ядро: %+v", items)
|
||||
// ИШ — у любой установки: ставится при подготовке из проверенного дистрибутива на сервере
|
||||
if items := prepareItems(rc, false, false, ""); len(items) != 3 || items[0].Title != "Обновление bj" || items[0].State != "wait" || items[1].State != "ok" || !strings.Contains(items[2].Title, "Интеграционный шлюз") {
|
||||
t.Fatalf("без лицензии — обновление (ещё не проверялось), ядро и ИШ: %+v", items)
|
||||
}
|
||||
// выпуск, который подготовка уже ставила, после перезапуска bj не ставится по кругу
|
||||
oldTried := prepareTriedFile
|
||||
@@ -730,3 +731,30 @@ func TestQuikModeAndCertHint(t *testing.T) {
|
||||
t.Fatal("ошибка сертификата распознаётся, ошибка логина — нет")
|
||||
}
|
||||
}
|
||||
|
||||
// Ход установки ИШ для «Подготовки модулей»: идёт — пока установщик обновляет файл; молчит 20 минут — прервалась.
|
||||
func TestIgateInstallStatus(t *testing.T) {
|
||||
old := igateInstallStatusPath
|
||||
igateInstallStatusPath = filepath.Join(t.TempDir(), "st.json")
|
||||
defer func() { igateInstallStatusPath = old }()
|
||||
if st := igateInstallStatus(); st.Busy || st.Stage != "" {
|
||||
t.Fatalf("нет файла: %+v", st)
|
||||
}
|
||||
write := func(stage string, age time.Duration) {
|
||||
_ = os.WriteFile(igateInstallStatusPath, []byte(`{"stage":"`+stage+`","msg":"шаг"}`), 0o644)
|
||||
at := time.Now().Add(-age)
|
||||
_ = os.Chtimes(igateInstallStatusPath, at, at)
|
||||
}
|
||||
write("package", time.Minute)
|
||||
if st := igateInstallStatus(); !st.Busy {
|
||||
t.Fatalf("идёт: %+v", st)
|
||||
}
|
||||
write("package", 25*time.Minute)
|
||||
if st := igateInstallStatus(); st.Busy || st.Stage != "error" || !strings.Contains(st.Msg, "прервалась") {
|
||||
t.Fatalf("прервалась: %+v", st)
|
||||
}
|
||||
write("done", time.Hour)
|
||||
if st := igateInstallStatus(); st.Busy || st.Stage != "done" {
|
||||
t.Fatalf("готово: %+v", st)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ package lkgateway
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
@@ -30,6 +31,7 @@ var prepareRun struct {
|
||||
Running, Finished bool
|
||||
Result, Err string
|
||||
UpdState, UpdText string // этап «Обновление bj»: пусто — ещё не проверялось в этом запуске bj
|
||||
IgateErr string // установку ИШ не удалось даже запустить
|
||||
}
|
||||
|
||||
// prepareBoot — метка запуска bj: страница видит перезапуск (после обновления ядра) и продолжает подготовку.
|
||||
@@ -86,6 +88,7 @@ func prepareItems(rc *RuntimeConfig, running, finished bool, runErr string) []Pr
|
||||
}
|
||||
out = append(out, it)
|
||||
}
|
||||
out = append(out, prepareIgateItem())
|
||||
if moduleAllowed(rc, "web-cabinet") {
|
||||
out = append(out, PrepareItem{Title: moduleTitles["web-cabinet"], State: "info", Detail: "ставится на веб-сервер организации: дистрибутив и ключ кабинета — в мастере, глава «Запуск»"})
|
||||
}
|
||||
@@ -169,7 +172,7 @@ func prepareStart(rc *RuntimeConfig) {
|
||||
prepareRun.Running, prepareRun.Finished, prepareRun.Result, prepareRun.Err = true, false, "", ""
|
||||
prepareRun.Unlock()
|
||||
go func() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 12*time.Minute)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 25*time.Minute)
|
||||
defer cancel()
|
||||
prepareUpdates(ctx, rc) // сначала — обновления (при обновлении ядра bj перезапустится, страница продолжит)
|
||||
var msg, errText string
|
||||
@@ -180,12 +183,73 @@ func prepareStart(rc *RuntimeConfig) {
|
||||
} else {
|
||||
msg = m
|
||||
}
|
||||
prepareIgate(ctx)
|
||||
prepareRun.Lock()
|
||||
prepareRun.Running, prepareRun.Finished, prepareRun.Result, prepareRun.Err = false, true, msg, errText
|
||||
prepareRun.Unlock()
|
||||
}()
|
||||
}
|
||||
|
||||
// prepareIgate — интеграционный шлюз НРД при подготовке: нет на сервере — поставить из дистрибутива, который
|
||||
// лежит на сервере (образ или обновление bj): это версия, проверенная ZETIT вместе с bj. С сайта НРД ИШ не
|
||||
// качается — новые версии приходят только с обновлением bj. Ход установки пишет установщик (igateInstallStatus).
|
||||
func prepareIgate(ctx context.Context) {
|
||||
if igateInstalled() || igateBundledVersion() == "" {
|
||||
return
|
||||
}
|
||||
if st := igateInstallStatus(); !st.Busy {
|
||||
res, err := runKeymediaOp(ctx, "igate-install", map[string]string{"extra": `{"bundled":"1"}`})
|
||||
if err == nil {
|
||||
if ok, _ := res["ok"].(bool); !ok {
|
||||
err = fmt.Errorf("%v", res["error"])
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
prepareRun.Lock()
|
||||
prepareRun.IgateErr = err.Error()
|
||||
prepareRun.Unlock()
|
||||
return
|
||||
}
|
||||
}
|
||||
for ctx.Err() == nil { // 2–5 минут: пакеты, пользователь igate, база, служба
|
||||
time.Sleep(3 * time.Second)
|
||||
if st := igateInstallStatus(); !st.Busy && st.Stage != "" {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// prepareIgateItem — строка «Интеграционный шлюз НРД» на экране подготовки.
|
||||
func prepareIgateItem() PrepareItem {
|
||||
it := PrepareItem{Title: "Интеграционный шлюз НРД (ИШ)"}
|
||||
st := igateInstallStatus()
|
||||
prepareRun.Lock()
|
||||
igErr := prepareRun.IgateErr
|
||||
prepareRun.Unlock()
|
||||
switch {
|
||||
case st.Busy:
|
||||
it.State, it.Detail = "run", "устанавливается: "+st.Msg+" (2–5 минут)"
|
||||
case igateInstalled():
|
||||
it.State, it.Detail = "ok", "установлен"+pickStr(prefixed(", версия ", ishInstalledVersion()), "")
|
||||
case st.Stage == "error":
|
||||
it.State, it.Detail = "err", "не установился: "+st.Msg+" — повторить можно в мастере, шаг «Шлюз НРД», кнопка «Установить ИШ заново»"
|
||||
case igErr != "":
|
||||
it.State, it.Detail = "err", "установка не запустилась: "+igErr+" — повторить можно в мастере, шаг «Шлюз НРД»"
|
||||
case igateBundledVersion() == "":
|
||||
it.State, it.Detail = "warn", "дистрибутива ИШ на сервере нет — он приходит с обновлением bj"
|
||||
default:
|
||||
it.State, it.Detail = "wait", "будет установлен, версия "+igateBundledVersion()
|
||||
}
|
||||
return it
|
||||
}
|
||||
|
||||
func prefixed(p, v string) string {
|
||||
if v == "" {
|
||||
return ""
|
||||
}
|
||||
return p + v
|
||||
}
|
||||
|
||||
// prepareUpdates — первый этап подготовки: есть обновление — поставить, нет — дальше.
|
||||
func prepareUpdates(ctx context.Context, rc *RuntimeConfig) {
|
||||
set := func(state, text string) {
|
||||
|
||||
@@ -528,12 +528,9 @@
|
||||
{{if eq .Step 7}}
|
||||
<div class="card">
|
||||
<h2><span class="dot {{if .Done.NSD}}ok{{else}}err{{end}}"></span>Шлюз НРД</h2>
|
||||
<p>Через интеграционный шлюз НРД (ИШ) на этом сервере bj отправляет переводы и получает ответы. bj ставит и настраивает шлюз сам; доступ к тестовому и боевому контуру НРД открывает по заявке — письма готовы.</p>
|
||||
<p>Через интеграционный шлюз НРД (ИШ) на этом сервере bj отправляет переводы и получает ответы. bj ставит и настраивает шлюз сам; доступ к тестовому и боевому контуру НРД открывает по заявке на своём сайте — как её подать, ниже.</p>
|
||||
{{template "igateInstall" .}}
|
||||
<details style="margin-top:4px"><summary style="cursor:pointer;color:var(--accent);font-size:13px">Письма в НРД о доступе к контурам</summary>
|
||||
{{template "letterCard" (letter "nsd-test")}}
|
||||
{{template "letterCard" (letter "nsd-prod")}}
|
||||
</details>
|
||||
<div class="wiz-info" style="margin:12px 0 0"><span><b>Доступ к контурам НРД</b> — заявкой на сайте НРД, письмами НРД не подключает. Страница сервиса <a href="https://www.nsd.ru/services/novye-servisy/moex-most-dlya-m2m/" target="_blank" rel="noopener">MOEX МОСТ для M2M</a>, раздел «Как подключиться»: заявка на тестирование — система МОСТ, тип «M2M Автотестирование», затем «M2M Подключение»; после тестирования — договор с НРД и боевой контур.</span></div>
|
||||
<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">
|
||||
|
||||
@@ -13,11 +13,12 @@
|
||||
<div class="card" style="margin:10px 0">
|
||||
<h3>Шлюз на этом сервере</h3>
|
||||
<div id="ig-head" class="muted">Проверяю…</div>
|
||||
<details id="ig-inst" style="margin-top:8px">
|
||||
<summary style="cursor:pointer;color:var(--accent)" id="ig-inst-sum">Установить или обновить ИШ</summary>
|
||||
<p class="muted" style="font-size:13px;margin:8px 0">Дистрибутив ИШ для Linux выпускает НРД. bj скачает его с сайта НРД сам или возьмёт загруженный архив <code>igate-…-amd64.zip</code>. Установка занимает 2–5 минут: пакеты для работы без графики, пользователь igate, база ИШ, служба. Настройки ИШ сохраняются, прежняя версия копируется в /var/backups.</p>
|
||||
<div id="ig-bundled" style="display:none;margin-bottom:10px"><button type="button" class="btn" id="ig-bnd">Установить с образа (без интернета)</button> <span class="muted" id="ig-bnd-v" style="font-size:12.5px"></span></div>
|
||||
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:end">
|
||||
<p class="muted" style="font-size:13px;margin:6px 0 0">Версия ИШ — та, что проверена ZETIT вместе с bj; новые версии приходят с обновлением bj. bj ставит шлюз сам при подготовке.</p>
|
||||
<div id="ig-reinst" style="display:none;margin-top:10px"><button type="button" class="btn" id="ig-bnd">Установить ИШ</button> <span class="muted" id="ig-bnd-v" style="font-size:12.5px"></span></div>
|
||||
<div id="ig-out" style="margin-top:8px;font-size:13px"></div>
|
||||
<pre id="ig-log" style="display:none;max-height:180px;overflow:auto;font-size:11px;margin-top:6px"></pre>
|
||||
{{if devOn}}<details style="margin-top:8px"><summary style="cursor:pointer;color:var(--accent);font-size:13px">Для разработчиков: другая версия ИШ</summary>
|
||||
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:end;margin-top:8px">
|
||||
<label class="f">Версия на сайте НРД<input id="ig-ver" value="107.0.930" style="width:140px"></label>
|
||||
<button type="button" class="btn" id="ig-dl">Скачать с сайта НРД и установить</button>
|
||||
</div>
|
||||
@@ -25,9 +26,7 @@
|
||||
<input type="file" id="ig-file" accept=".zip,.deb">
|
||||
<button type="button" class="btn ghost" id="ig-up">Установить из файла</button>
|
||||
</div>
|
||||
<div id="ig-out" style="margin-top:8px;font-size:13px"></div>
|
||||
<pre id="ig-log" style="display:none;max-height:180px;overflow:auto;font-size:11px;margin-top:6px"></pre>
|
||||
</details>
|
||||
</details>{{end}}
|
||||
</div>
|
||||
|
||||
<div class="card" style="margin:10px 0">
|
||||
@@ -58,16 +57,22 @@
|
||||
fetch('/admin/setup/igate/status',{cache:'no-store'}).then(function(r){return r.json()}).then(function(d){
|
||||
st=d;
|
||||
if(!d.ok){ head.innerHTML=bad(d.error); return; }
|
||||
if(!d.installed){ head.innerHTML=bad('ИШ на этом сервере не установлен')+' — установите его ниже.'; document.getElementById('ig-inst').open=true; }
|
||||
else head.innerHTML=(d.engine==='Running'?ok('ИШ '+d.version+' работает'):bad('ИШ '+d.version+' установлен, движок: '+(d.engine||'не отвечает')+', служба: '+d.service));
|
||||
if(d.installed) document.getElementById('ig-inst-sum').textContent='Обновить ИШ (сейчас '+d.version+')';
|
||||
if(d.bundled){ document.getElementById('ig-bundled').style.display='block'; document.getElementById('ig-bnd-v').textContent='версия '+d.bundled+' — дистрибутив НРД записан на сервер при установке с образа'; }
|
||||
var ri=document.getElementById('ig-reinst'), bb=document.getElementById('ig-bnd'), bv=document.getElementById('ig-bnd-v');
|
||||
var running=d.installed&&d.engine==='Running';
|
||||
if(!d.installed) head.innerHTML=bad('ИШ на этом сервере не установлен');
|
||||
else head.innerHTML=(running?ok('ИШ '+d.version+' установлен и работает'):bad('ИШ '+d.version+' установлен, но не работает: движок '+(d.engine||'не отвечает')+', служба '+d.service));
|
||||
// не стоит или не работает — одна кнопка: поставить заново проверенную версию с сервера (систему переставлять не нужно)
|
||||
ri.style.display=running?'none':'block';
|
||||
if(!running){
|
||||
if(d.bundled){ bb.style.display=''; bb.textContent=d.installed?'Установить ИШ заново':'Установить ИШ'; bv.textContent='версия '+d.bundled+' — проверена ZETIT, дистрибутив на сервере'; }
|
||||
else { bb.style.display='none'; bv.textContent='Дистрибутива ИШ на сервере нет — он приходит с обновлением bj («Настройка → Обновления»).'; }
|
||||
}
|
||||
var dep=d.deponent||'', cr=document.getElementById('ig-crypto').value;
|
||||
var rows=[['test3','Тестовый TEST3'],['prod','Боевой']].map(function(x){
|
||||
var code=(x[0]==='prod'?'PROD':'TEST3')+dep, ch=(d.channels||[]).filter(function(c){return c.code===code})[0], act=d.active_contour===x[0];
|
||||
var s=[], a=[];
|
||||
if(!dep) s.push(bad('нет депозитарного кода (мастер → «Организация»)'));
|
||||
else if(!ch){ s.push('<span class="muted">канала в ИШ нет</span>'); if(d.installed) a.push('<button type="button" class="btn sm" data-act="contour" data-c="'+x[0]+'">Настроить канал</button>'); }
|
||||
else if(!ch){ if(d.installed){ s.push('<span class="muted">канала в ИШ нет — bj создаст его: адрес и сертификат контура НРД известны</span>'); a.push('<button type="button" class="btn sm" data-act="contour" data-c="'+x[0]+'">Создать канал</button>'); } else s.push('<span class="muted">канал создаётся после установки ИШ</span>'); }
|
||||
else {
|
||||
s.push(ok('канал '+ch.code)+' <span class="muted" style="font-size:12px">'+esc(ch.crypto+' · '+ch.endpoint)+'</span>');
|
||||
s.push(ch.profile?ok('ключ: профиль '+ch.profile):bad('ключ этого контура не установлен'));
|
||||
@@ -108,12 +113,14 @@
|
||||
if(Date.now()-t0>1200000) clearInterval(iv); },3000); }
|
||||
document.getElementById('ig-bnd').onclick=function(){ var p=new URLSearchParams(); p.set('bundled','1');
|
||||
out.textContent='Запускаю…'; post('/admin/setup/igate/install',p).then(function(d){ if(!d.ok){out.innerHTML=bad(d.error);return} out.textContent=d.output; poll(); }); };
|
||||
document.getElementById('ig-dl').onclick=function(){ var p=new URLSearchParams(); p.set('version',document.getElementById('ig-ver').value.trim());
|
||||
if(document.getElementById('ig-dl')) document.getElementById('ig-dl').onclick=function(){ var p=new URLSearchParams(); p.set('version',document.getElementById('ig-ver').value.trim());
|
||||
out.textContent='Запускаю…'; post('/admin/setup/igate/install',p).then(function(d){ if(!d.ok){out.innerHTML=bad(d.error);return} out.textContent=d.output; poll(); }); };
|
||||
document.getElementById('ig-up').onclick=function(){ var f=document.getElementById('ig-file').files[0]; if(!f){ out.textContent='Выберите файл igate-…-amd64.zip'; return; }
|
||||
if(document.getElementById('ig-up')) document.getElementById('ig-up').onclick=function(){ var f=document.getElementById('ig-file').files[0]; if(!f){ out.textContent='Выберите файл igate-…-amd64.zip'; return; }
|
||||
var fd=new FormData(); fd.append('file',f); out.textContent='Загружаю '+f.name+'…';
|
||||
fetch('/admin/setup/igate/install',{method:'POST',body:fd}).then(function(r){return r.json()}).then(function(d){ if(!d.ok){out.innerHTML=bad(d.error);return} out.textContent=d.output; poll(); }); };
|
||||
load();
|
||||
// установка уже идёт (например, её начала «Подготовка модулей») — показать ход
|
||||
fetch('/admin/setup/igate/install-status',{cache:'no-store'}).then(function(r){return r.json()}).then(function(s){ if(s.stage&&s.stage!=='done'&&s.stage!=='error'){ out.textContent='Установка: '+s.msg; poll(); } }).catch(function(){});
|
||||
})();
|
||||
</script>
|
||||
{{end}}
|
||||
|
||||
Reference in New Issue
Block a user