From 6b490986e402a7fe8014f99abc06bfb6845bd0b3 Mon Sep 17 00:00:00 2001 From: zuevav Date: Wed, 7 Oct 2026 08:19:34 +0300 Subject: [PATCH] =?UTF-8?q?=C2=AB=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F=C2=BB=20=E2=80=94=20=D0=BE=D0=B4=D0=BD?= =?UTF-8?q?=D0=B0=20=D0=BA=D0=B0=D1=80=D1=82=D0=BE=D1=87=D0=BA=D0=B0=20?= =?UTF-8?q?=D1=81=D0=BE=D1=81=D1=82=D0=BE=D1=8F=D0=BD=D0=B8=D1=8F=20=D0=B8?= =?UTF-8?q?=20=D0=BE=D0=B4=D0=BD=D0=B0=20=D0=BA=D0=BD=D0=BE=D0=BF=D0=BA?= =?UTF-8?q?=D0=B0;=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA=D0=B0=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B8=20=D0=BE=D1=82=D0=BA=D1=80=D1=8B=D1=82=D0=B8?= =?UTF-8?q?=D0=B8=20=D1=80=D0=B0=D0=B7=D0=B4=D0=B5=D0=BB=D0=B0;=20=C2=AB?= =?UTF-8?q?=D0=A1=D0=BB=D1=83=D0=B6=D0=B1=D1=8B=C2=BB=20=E2=80=94=20=D0=B2?= =?UTF-8?q?=20=C2=AB=D0=94=D0=B8=D0=B0=D0=B3=D0=BD=D0=BE=D1=81=D1=82=D0=B8?= =?UTF-8?q?=D0=BA=D0=B5=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Сверху: «Установлены последние версии» (когда проверено) или «Доступно обновление X — Установить обновление» (всё сразу), или «Проверить не удалось»; кнопка «Проверить сейчас». - При открытии раздела bj проверяет обновления сам, если последней проверке больше 10 минут (и не чаще раза в 10 минут из браузера — без зацикливания, если источник не настроен); check отвечает JSON для экрана. - «Компоненты и версии» (обновить один компонент) и «Расширенные настройки» (обновление из файла, источник и канал, ключ, автопроверка) — под раскрытием. - «Службы» с перезапуском перенесены в «Диагностику»; подсказка в письме о службе — туда же. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi --- internal/lkgateway/setup.go | 8 ++ internal/lkgateway/unitswatch.go | 2 +- .../lkgateway/web/templates/admin_setup.html | 88 +++++++++++++------ internal/lkgateway/web/templates/layout.html | 19 ++++ 4 files changed, 87 insertions(+), 30 deletions(-) diff --git a/internal/lkgateway/setup.go b/internal/lkgateway/setup.go index 8c34079..2e0054a 100644 --- a/internal/lkgateway/setup.go +++ b/internal/lkgateway/setup.go @@ -1581,6 +1581,14 @@ func (h *setupHandlers) checkUpdate(w http.ResponseWriter, r *http.Request) { return } st, err := NewUpdater(h.rc).CheckForUpdate(r.Context()) + if r.Header.Get("X-BJ-Ajax") != "" { // раздел «Обновления» проверяет сам при открытии + msg := st.Message + if err != nil { + msg = err.Error() + } + jsonOut(w, map[string]any{"ok": err == nil, "has_update": st.HasUpdate, "available": st.Available, "msg": msg}) + return + } if err != nil { setupFlash(w, r, "Проверка обновлений: "+err.Error()) return diff --git a/internal/lkgateway/unitswatch.go b/internal/lkgateway/unitswatch.go index 16d35cb..db2e7d1 100644 --- a/internal/lkgateway/unitswatch.go +++ b/internal/lkgateway/unitswatch.go @@ -61,7 +61,7 @@ func watchedUnits(rc *RuntimeConfig) []watchedUnit { st += ", с " + v.Since } out = append(out, watchedUnit{Key: v.Unit, Title: v.Title, Unit: v.Unit, OK: v.OK(), State: st, - Hint: "Перезапустить — Настройка → Обновления → «Службы», кнопка «Перезапустить»; если не поднимается — journalctl -u " + v.Unit + "."}) + Hint: "Перезапустить — Настройка → Диагностика → «Службы», кнопка «Перезапустить»; если не поднимается — journalctl -u " + v.Unit + "."}) } // служба ключей — когда она нужна: включена в автозапуск (bj включает её при установке ключа) или уже работала; // на свежем сервере до установки ключа её может и не быть — это не сбой diff --git a/internal/lkgateway/web/templates/admin_setup.html b/internal/lkgateway/web/templates/admin_setup.html index 858b6f2..753b56c 100644 --- a/internal/lkgateway/web/templates/admin_setup.html +++ b/internal/lkgateway/web/templates/admin_setup.html @@ -803,6 +803,22 @@

Диагностика

+
+

Службы

+

Ядро и модули работают отдельными службами: если служба модуля не запущена, его раздел в bj отвечает ошибкой связи. Здесь видно состояние и можно перезапустить службу, не заходя на сервер.

+
+ + {{range .Units}} + + + + + + {{end}}
СлужбаЧто делаетСостояниеС
{{.Unit}}{{.Title}}{{if .OK}}работает{{else}}{{.State}}{{with .Sub}} / {{.}}{{end}}{{with .Result}} ({{.}}){{end}}{{end}}{{if .Since}}{{.Since}}{{else}}—{{end}}
+ +
+
+

Здоровье сервиса

Фоновые компоненты под самоконтролем: при сбое перезапускаются автоматически, при зависании — перезапуск всего сервиса и запись в «Новости». Ниже — живой статус.

@@ -903,25 +919,40 @@

Обновления

{{template "verline" (.Versions.Get "bj-server")}} -
-

Службы

-

Ядро и модули работают отдельными службами: если служба модуля не запущена, его раздел в bj отвечает ошибкой связи. Здесь видно состояние и можно перезапустить службу, не заходя на сервер.

-
- - {{range .Units}} - - - - - - {{end}}
СлужбаЧто делаетСостояниеС
{{.Unit}}{{.Title}}{{if .OK}}работает{{else}}{{.State}}{{with .Sub}} / {{.}}{{end}}{{with .Result}} ({{.}}){{end}}{{end}}{{if .Since}}{{.Since}}{{else}}—{{end}}
- -
+
+ {{if .UpdatesAvailable}} +
Доступно обновление{{with .Settings.Update.Available}} {{.}}{{end}}{{if .Settings.Update.Notes}}{{.Settings.Update.Notes}}{{else}}новые версии компонентов — список ниже, в «Компоненты и версии»{{end}}
+
+ {{else if and (not .Settings.Update.LastCheck.IsZero) .UpdateFailed}} +
Проверить обновления не удалось{{.Settings.Update.LastResult}} · {{msk .Settings.Update.LastCheck}}
+
+ {{else}} +
Установлены последние версии{{if .Settings.Update.LastCheck.IsZero}}ещё не проверялось{{else}}проверено {{msk .Settings.Update.LastCheck}}{{end}}{{if .Settings.Update.AutoCheck}} · bj проверяет сам каждые 6 часов{{end}}
+
+ {{end}} + {{if and .License.Present (not .License.AllowsUpdates)}}

В лицензии нет модуля «Обновления».

{{end}}
+
-

Компоненты

-

Каждый компонент обновляется отдельно: своя версия в релизе, перезапускается только его служба. Файлы ставит root-установщик после повторной проверки подписи и контрольных сумм. Модули, которых нет на этом сервере, обновлением не ставятся — кроме выделенных из ядра (НПР / УДС, экспорт QUIK): если модуль входит в лицензию, он ставится вместе с обновлением, иначе его раздел перестал бы работать.

+
+ Компоненты и версиичто установлено на этом сервере и что есть в выпуске; можно обновить один компонент
@@ -934,24 +965,22 @@ {{end}}
КомпонентУстановленаДоступнаСлужба
-
- {{if .UpdatesAvailable}}
{{end}} -
+
+
+ +
+
+ Расширенные настройкиисточник и канал обновлений, обновление из файла для сервера без выхода в интернет +

Обновление из файла

+

Закрытый контур. Если у сервера нет выхода к серверу обновлений, скачайте пакет обновления на портале клиента (lic.zetit.ru → Установка → «Пакет обновления») и загрузите его кнопкой «Обновить из файла». Система сама распознаёт, что внутри — ядро, модули или всё вместе — и ставит в правильном порядке. Модуль, которому нужно более новое ядро, чем установлено, будет пропущен с подсказкой обновить сначала ядро.

+
-
- {{if .Settings.Update.Notes}}

Что нового в релизе {{.Settings.Update.Available}}: {{.Settings.Update.Notes}}

{{end}} -

Закрытый контур. Если у сервера нет выхода к серверу обновлений, скачайте пакет обновления на портале клиента (lic.zetit.ru → Установка → «Пакет обновления») и загрузите его кнопкой «Обновить из файла». Система сама распознаёт, что внутри — ядро, модули или всё вместе — и ставит в правильном порядке. Модуль, которому нужно более новое ядро, чем установлено, будет пропущен с подсказкой обновить сначала ядро.

- {{if not .Settings.Update.LastCheck.IsZero}}{{if .UpdateFailed}}
{{.Settings.Update.LastResult}} ({{.Settings.Update.LastCheck.Format "02.01.2006 15:04"}})
{{else}}

Последняя проверка: {{.Settings.Update.LastCheck.Format "02.01.2006 15:04"}}. {{.Settings.Update.LastResult}}

{{end}}{{end}} - {{if and .License.Present (not .License.AllowsUpdates)}}

В лицензии нет модуля «Обновления».

{{end}} -
- -
-

Источник обновлений

-

Обновления раздаёт сервер лицензий по лицензии этой установки: модуль скачивается, только если он входит в лицензию. Каждый релиз проверяется по подписи Ed25519 и контрольным суммам, без действительной подписи установка не выполняется.

+
+

Источник обновлений

@@ -965,6 +994,7 @@
+
diff --git a/internal/lkgateway/web/templates/layout.html b/internal/lkgateway/web/templates/layout.html index 348c47e..e1c3e09 100644 --- a/internal/lkgateway/web/templates/layout.html +++ b/internal/lkgateway/web/templates/layout.html @@ -378,6 +378,25 @@ label { font-size:13px; font-weight:500; } .quik-term b{color:var(--text);font-weight:600} .quik-term input{margin-top:3px;flex:none} .org-grid{grid-template-columns:minmax(0,1fr) minmax(0,1fr) minmax(0,2fr);gap:10px;max-width:none} +/* карточка состояния (обновления и др.): значок, что происходит, действие справа */ +.stc{display:flex;gap:14px;align-items:center;padding:14px 18px;border-radius:12px;border:1px solid var(--border);background:var(--card-2)} +.stc-ic{flex:none;position:relative;width:28px;height:28px;border-radius:50%;background:var(--border-strong)} +.stc-b{flex:1;min-width:0} +.stc-b b{display:block;font-size:15px;font-weight:650} +.stc-b small{display:block;color:var(--muted);font-size:13px;line-height:1.4;margin-top:2px;overflow-wrap:anywhere} +.stc form, .stc .btn{flex:none} +.stc-ok{background:color-mix(in srgb,var(--ok) 9%,var(--card));border-color:color-mix(in srgb,var(--ok) 40%,var(--border))} +.stc-ok .stc-ic{background:var(--ok)} +.stc-ok .stc-ic::after{content:"";position:absolute;left:50%;top:45%;width:6px;height:12px;border:solid #fff;border-width:0 2.2px 2.2px 0;transform:translate(-50%,-50%) rotate(45deg)} +.stc-warn{background:color-mix(in srgb,var(--accent) 8%,var(--card));border-color:color-mix(in srgb,var(--accent) 40%,var(--border))} +.stc-warn .stc-ic{background:var(--accent)} +.stc-warn .stc-ic::after{content:"↑";position:absolute;inset:0;display:grid;place-items:center;color:#fff;font-weight:700;font-size:15px} +.stc-err{background:var(--warn-weak);border-color:color-mix(in srgb,var(--warn) 45%,var(--border))} +.stc-err .stc-ic{background:var(--warn)} +.stc-err .stc-ic::after{content:"!";position:absolute;inset:0;display:grid;place-items:center;color:#fff;font-weight:700;font-size:15px} +.stc-run .stc-ic{background:none;border:2.5px solid var(--accent-weak);border-top-color:var(--accent);box-sizing:border-box;animation:dq-spin .8s linear infinite} +.stc-run .btn, .stc-run form{visibility:hidden} +@media (max-width:600px){.stc{flex-wrap:wrap}} @media (max-width:700px){.org-grid{grid-template-columns:minmax(0,1fr)}} @media (max-width:820px) { .settings { grid-template-columns:1fr; }