Files
Bridge-and-Join-s/internal/lkgateway/igatecontour.go
T
zuevav 4adc1398f6 База bj на своём PostgreSQL; «Не заполнено: …» для пустых полей; запись канала ИШ — в фоне, с ходом на странице
- шаг «База bj»: перенос на сервер PostgreSQL организации (проверка подключения, версии, прав, пустой базы;
  pg_dump → psql; сверка числа строк по таблицам; переключение и перезапуск); прежняя база — запасная копия;
  резервные копии: дамп внешней базы по её подключению, восстановление её не трогает
- все формы: пустые видимые обязательные поля называются по имени и подсвечиваются; DEPO — и на сервере
- «Обновить/Создать канал»: в фоне bj, ход по этапам с таймером (и после перезагрузки страницы), переходы
  заблокированы до конца, в конце проверка, что шлюз загрузил канал; состояние ИШ во время записи — без
  очереди к root-обработчику (страница больше не висит на «Проверяю…»)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
2026-10-07 00:00:35 +03:00

154 lines
5.5 KiB
Go

package lkgateway
// Запись канала контура НРД в ИШ (06.10.2026). Root-обработчик пишет канал в базу ИШ и перезапускает шлюз — до
// двух минут. Раньше это делал сам запрос страницы: перезагрузка страницы прерывала ожидание, а запрос
// состояния ИШ вставал в очередь за записью канала — страница висела на «Проверяю…» без объяснений, и человек
// мог уйти «Далее» посреди перезапуска. Теперь запись идёт в фоне bj, страница показывает её ход (и после
// перезагрузки), а в конце bj проверяет, что шлюз действительно загрузил канал.
import (
"context"
"strings"
"sync"
"time"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/nsdadapter"
)
// igateContourView — ход записи канала для страницы.
type igateContourView struct {
Running bool `json:"running"`
Contour string `json:"contour,omitempty"`
Stage string `json:"stage,omitempty"` // write | engine | channel | done
Started int64 `json:"started,omitempty"`
Done bool `json:"done,omitempty"`
OK bool `json:"ok"`
Msg string `json:"msg,omitempty"`
At int64 `json:"at,omitempty"`
}
type igateContourState struct {
sync.Mutex
v igateContourView
}
var igateContourJob igateContourState
func (j *igateContourState) start(contour string) bool {
j.Lock()
defer j.Unlock()
if j.v.Running {
return false
}
j.v = igateContourView{Running: true, Contour: contour, Stage: "write", Started: time.Now().Unix(), OK: true}
return true
}
func (j *igateContourState) stage(st string) {
j.Lock()
j.v.Stage = st
j.Unlock()
}
func (j *igateContourState) finish(ok bool, msg string) {
j.Lock()
j.v.Running, j.v.Done, j.v.OK, j.v.Msg, j.v.Stage, j.v.At = false, true, ok, msg, "done", time.Now().Unix()
j.Unlock()
}
func (j *igateContourState) view() igateContourView {
j.Lock()
defer j.Unlock()
v := j.v
if v.Done && time.Since(time.Unix(v.At, 0)) > 15*time.Minute {
return igateContourView{OK: true} // итог давний — не показываем
}
return v
}
// igateLastStatus — последнее состояние ИШ от root-обработчика (отдаётся, пока он занят записью канала).
var igateLastStatus lastStatus
type lastStatus struct {
sync.Mutex
m map[string]any
}
func (l *lastStatus) set(m map[string]any) {
l.Lock()
l.m = m
l.Unlock()
}
func (l *lastStatus) get() map[string]any {
l.Lock()
defer l.Unlock()
if l.m == nil {
return nil
}
out := map[string]any{}
for k, v := range l.m {
out[k] = v
}
return out
}
// igateContourRun — запись канала контура в ИШ и проверка, что шлюз его загрузил (в фоне, без запроса страницы).
func igateContourRun(rc *RuntimeConfig, contour, crypto, dep string) {
extra := `{"contour":"` + contour + `","crypto":"` + crypto + `","deponent":"` + dep + `"}`
ctx, cancel := context.WithTimeout(context.Background(), 4*time.Minute)
defer cancel()
go func() { // этап «перезапуск ИШ» — root-обработчик пишет канал за секунды и дальше ждёт движок
time.Sleep(4 * time.Second)
if v := igateContourJob.view(); v.Running && v.Stage == "write" {
igateContourJob.stage("engine")
}
}()
res, err := runKeymediaOp(ctx, "igate-contour", map[string]string{"extra": extra})
if err != nil || res["ok"] != true {
msg := errStr(err)
if e, ok := res["error"].(string); ok && e != "" {
msg = e
}
igateContourJob.finish(false, msg)
return
}
out := asString(res["output"])
code := strings.ToUpper(contour) + dep
if prof, err := nsdadapter.LookupProfile(contour + "-" + crypto); err == nil {
code = prof.Channel + dep
}
enabled := strings.Contains(out, "включён")
if enabled { // канал с ключом: шлюз должен его загрузить — иначе любой обмен даёт HTTP 500
igateContourJob.stage("channel")
base := pickStr(rc.Snapshot().NSD.IGWBaseURL, igateDefaultURL)
loaded := false
for i := 0; i < 20 && !loaded; i++ {
if found, en, err := igateChannelState(ctx, base, code); err == nil && found && en {
loaded = true
break
}
time.Sleep(3 * time.Second)
}
if !loaded {
igateContourJob.finish(false, "канал "+code+" записан, но шлюз его не загрузил — журнал ИШ: /var/lib/igate/.local/share/NSD Integration Gateway/logs-svc/log.txt")
return
}
out += "; шлюз загрузил канал " + code
}
if rc.Snapshot().NSD.Profile == "" {
// контур в bj ещё не выбран (новая установка) — первый настроенный канал и есть рабочий
n := rc.Snapshot().NSD
n.Profile, n.IGWBaseURL = contour+"-"+crypto, pickStr(n.IGWBaseURL, igateDefaultURL)
if rc.UpdateNSD(n) == nil {
out += ". bj работает с этим контуром и перезапускается — страница обновится сама"
go func() {
time.Sleep(3 * time.Second)
_, _ = restartUnit(context.Background(), "bj-server")
}()
}
}
refreshCertStore(rc)
igateContourJob.finish(true, out)
}