diff --git a/deploy/linux/sbin/bj-backup-admin b/deploy/linux/sbin/bj-backup-admin index 9d8fdf8..7779524 100755 --- a/deploy/linux/sbin/bj-backup-admin +++ b/deploy/linux/sbin/bj-backup-admin @@ -19,7 +19,8 @@ err() { echo "{\"ok\":false,\"error\":$(q "$1")}"; exit 0; } [ -r "$CONF" ] && . "$CONF" # хранилище из запроса (восстановление на новом сервере, проверка до сохранения) или из настроек SHARE_IN=$(j share); DIR_IN=$(j dir); USER_IN=$(j user); PASS_IN=$(j password) -SHARE=${SHARE_IN:-${SHARE:-}}; DIR=${DIR_IN:-${DIR:-}} +# хранилище из запроса — и подпапка из запроса (не из прежних настроек: иначе список копий искал не там) +if [ -n "$SHARE_IN" ]; then SHARE=$SHARE_IN; DIR=$DIR_IN; else SHARE=${SHARE:-}; DIR=${DIR:-}; fi # smbloc — адрес хранилища, как его вводят (\\nas\backup\bj как в проводнике, //nas/backup, smb://…, сервер # в «Хранилище» + общая папка в «Папке») → две строки: общая папка для smbclient (//сервер/папка) и путь внутри неё smbloc(){ python3 - "$1" "$2" <<'PY' diff --git a/internal/lkgateway/admin.go b/internal/lkgateway/admin.go index 23bf602..b707e75 100644 --- a/internal/lkgateway/admin.go +++ b/internal/lkgateway/admin.go @@ -60,6 +60,7 @@ var templateFuncs = template.FuncMap{ "backupStatus": backupStatus, "backupWhen": backupWhen, "smbWin": smbWin, + "smbView": smbView, "pgLocal": pgDSNLocal, "whForm": func(w WorkHoursSettings, ret string) map[string]any { return map[string]any{"W": w, "Ret": ret} }, "weekdayNames": func() []string { return weekdayShort }, diff --git a/internal/lkgateway/backup.go b/internal/lkgateway/backup.go index 077b175..7be7027 100644 --- a/internal/lkgateway/backup.go +++ b/internal/lkgateway/backup.go @@ -86,6 +86,16 @@ func smbWin(share string) string { return strings.ReplaceAll(share, "/", `\`) } +// smbView — адрес хранилища для формы: сетевая папка как в проводнике и подпапка (сохранённые прежними +// версиями «\\сервер\» + «папка» показываются уже разобранными). +func smbView(share, dir string) map[string]string { + sh, d := smbLocation(share, dir) + if sh == "" { + return map[string]string{"Share": share, "Dir": dir} + } + return map[string]string{"Share": smbWin(sh), "Dir": pickStr(d, "bj-backup")} +} + // smbShareOK — в адресе есть и сервер, и общая папка. func smbShareOK(share string) bool { return strings.Count(strings.TrimPrefix(share, "//"), "/") == 1 } @@ -129,10 +139,10 @@ func backupParams(r *http.Request) map[string]string { } } if p["share"] != "" { // адрес как в проводнике → общая папка и путь внутри неё + // подпапка не указана — та же, что у копий по умолчанию (bj-backup): иначе root подставлял папку из + // прежних настроек и «Копии на хранилище» искала не там, куда легла копия p["share"], p["dir"] = smbLocation(p["share"], p["dir"]) - if p["dir"] == "" { - delete(p, "dir") - } + p["dir"] = pickStr(p["dir"], "bj-backup") } return p } diff --git a/internal/lkgateway/igate.go b/internal/lkgateway/igate.go index 6267c93..e3da48a 100644 --- a/internal/lkgateway/igate.go +++ b/internal/lkgateway/igate.go @@ -166,6 +166,7 @@ func registerIgateAdmin(mux *http.ServeMux, rc *RuntimeConfig) { res["depo_db"] = s.Fancy.DepoDB() res["depo_db_test"], res["depo_db_prod"] = s.NSD.DepoDBTest, s.NSD.DepoDBProd res["bundled"] = igateBundledVersion() + res["keymedia"] = keymediaState() // missing — ИШ ждёт флешку с ключом: переустановка не поможет jsonOut(w, res) }) // установка или обновление ИШ: загруженный архив НРД или версия для скачивания с nsd.ru diff --git a/internal/lkgateway/igwwatch.go b/internal/lkgateway/igwwatch.go index 49f1afd..5860f02 100644 --- a/internal/lkgateway/igwwatch.go +++ b/internal/lkgateway/igwwatch.go @@ -18,6 +18,8 @@ import ( "log" "net/http" "net/url" + "os" + "path/filepath" "strings" "sync" "time" @@ -45,6 +47,9 @@ type igwWatchState struct { LastRestart time.Time LastRemind time.Time RestartMsg string + // KeyWait — ИШ ждёт флешку с ключом подписи (окно Валидаты «Вставьте ключевой носитель»): перезапуск не + // поможет, пока её нет, а когда она появится — bj перезапустит ИШ сам. + KeyWait bool } var igwWatch igwWatchState @@ -155,6 +160,23 @@ func igwWatchTick(ctx context.Context, rc *RuntimeConfig) { needAlert := !w.Alerted && down >= igwAlertAfter canRestart := igwLocal(base) && down >= igwAlertAfter && w.Restarts < igwRestartMax && (w.LastRestart.IsZero() || now.Sub(w.LastRestart) >= igwRestartEvery) + keyAlert := false + if igwLocal(base) { + switch keymediaState() { + case "missing": // без флешки ИШ ждёт её в окне Валидаты — перезапуски бесполезны + canRestart = false + if !w.KeyWait { + w.KeyWait, keyAlert = true, true + needAlert = false + w.Alerted, w.LastRemind = true, now + } + case "ok": + if w.KeyWait { // флешку вернули — перезапустить ИШ сразу, попытки — заново + w.KeyWait, w.Restarts, w.LastRestart = false, 0, time.Time{} + canRestart = true + } + } + } remind := w.Alerted && !canRestart && w.Restarts >= igwRestartMax && now.Sub(w.LastRemind) >= igwRemindEvery if needAlert { w.Alerted, w.LastRemind = true, now @@ -169,6 +191,9 @@ func igwWatchTick(ctx context.Context, rc *RuntimeConfig) { attempt, since, lastErr := w.Restarts, w.DownSince, w.LastErr w.mu.Unlock() + if keyAlert { + raiseSystemAlert(rc, "ИШ НРД ждёт флешку с ключом подписи", fmt.Sprintf("Интеграционный шлюз НРД не запускается: флешки с ключом подписи в сервере нет (%s пуста) — Валидата ждёт ключевой носитель. Вставьте флешку с ключом в этот сервер: bj перезапустит шлюз сам. Переустанавливать ИШ не нужно.", keymediaDir)) + } if needAlert { plan := "ИШ на другом сервере — перезапустите службу ИШ там." if igwLocal(base) { @@ -186,6 +211,21 @@ func igwWatchTick(ctx context.Context, rc *RuntimeConfig) { } } +// keymediaDir — куда подключается флешка с ключом для ИШ (bj-keymedia-install, строка fstab по UUID). +const keymediaDir = "/var/lib/igate/keymedia" + +// keymediaState — флешка с ключом для ИШ: "" — не настроена (ключ не с флешки), "ok" — на месте, "missing" — нет. +func keymediaState() string { + b, err := os.ReadFile("/etc/fstab") + if err != nil || !strings.Contains(string(b), " "+keymediaDir+" ") { + return "" + } + if m, _ := filepath.Glob(keymediaDir + "/vdkeys/*.vdk"); len(m) > 0 { + return "ok" + } + return "missing" +} + // igwTryRestart — перезапуск службы ИШ и проверка, поднялся ли он. func igwTryRestart(rc *RuntimeConfig, base string, attempt int) { log.Printf("igw-watch: перезапуск службы %s (попытка %d из %d)", igwServiceUnit, attempt, igwRestartMax) diff --git a/internal/lkgateway/web/templates/admin_wizard.html b/internal/lkgateway/web/templates/admin_wizard.html index f8b2183..0570679 100644 --- a/internal/lkgateway/web/templates/admin_wizard.html +++ b/internal/lkgateway/web/templates/admin_wizard.html @@ -583,13 +583,14 @@
Через интеграционный шлюз НРД (ИШ) на этом сервере bj отправляет переводы и получает ответы. bj ставит и настраивает шлюз сам; доступ к тестовому и боевому контуру НРД открывает по заявке на своём сайте — как её подать, ниже.
{{template "igateInstall" .}}bj работает со шлюзом, который сам поставил на этом сервере: адрес — http://localhost:8090, менять его не нужно. Меняйте, только если у организации уже есть свой интеграционный шлюз НРД на другом сервере и bj должен работать через него: укажите адрес его веб-API (обычно порт 8090). Ключ подписи и каналы тогда настраиваются на том шлюзе.