Модуль, снятый с лицензии, отключается один раз: служба остановлена (bj-keymedia unit-disable), одно оповещение, в списках не показывается; вернули в лицензию — служба включается
Также: версия файла, совпадающего с выпуском, записывается (нет ложного «новее»); признак неудачной проверки отдельно от результатов установки; окно обновления само перезагружает страницу. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
This commit is contained in:
@@ -10,9 +10,12 @@ package lkgateway
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -95,6 +98,7 @@ func (u *Updater) CheckForUpdate(ctx context.Context) (UpdateStatus, error) {
|
||||
return st, err
|
||||
}
|
||||
st.Available, st.Notes = m.Version, m.Notes
|
||||
syncSameFileVersions(m)
|
||||
st.Components = release.Plan("", m, release.LoadInstalled(release.InstalledFile), BuildVersion)
|
||||
release.MarkSplitModules(st.Components, u.allowed)
|
||||
off := markUnlicensed(st.Components, u.allowed)
|
||||
@@ -121,9 +125,59 @@ func (u *Updater) CheckForUpdate(ctx context.Context) (UpdateStatus, error) {
|
||||
return st, nil
|
||||
}
|
||||
|
||||
// syncSameFileVersions — если файл компонента на сервере побайтно совпадает с файлом выпуска, а
|
||||
// записана другая версия (компонент не менялся, а номер выпуска вырос, или файл выложен вручную),
|
||||
// записываем версию выпуска: иначе список предлагает «обновить» то, что установщик отклонит как
|
||||
// «обновление не требуется».
|
||||
func syncSameFileVersions(m *release.Manifest) {
|
||||
installed := release.LoadInstalled(release.InstalledFile)
|
||||
changed := false
|
||||
for _, a := range m.Artifacts {
|
||||
c, ok := release.ComponentByName(a.Name)
|
||||
if !ok || a.SHA256 == "" {
|
||||
continue
|
||||
}
|
||||
v := a.Version
|
||||
if v == "" {
|
||||
v = m.Version
|
||||
}
|
||||
if cur, ok := installed[a.Name]; ok && cur.Version == v {
|
||||
continue
|
||||
}
|
||||
sum, err := fileSHA256(c.Path)
|
||||
if err != nil || !strings.EqualFold(sum, a.SHA256) {
|
||||
continue
|
||||
}
|
||||
installed[a.Name] = release.Installed{Version: v, SHA256: sum, InstalledAt: time.Now().UTC()}
|
||||
changed = true
|
||||
}
|
||||
if changed {
|
||||
if err := release.SaveInstalled(release.InstalledFile, installed); err != nil {
|
||||
log.Printf("lk-gateway: запись версий совпадающих компонентов: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fileSHA256(path string) (string, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
h := sha256.New()
|
||||
if _, err := io.Copy(h, f); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hex.EncodeToString(h.Sum(nil)), nil
|
||||
}
|
||||
|
||||
// markUnlicensed — модули, установленные на сервере, но снятые с лицензии: не обновляются (сервер
|
||||
// обновлений их файлы не отдаёт), в списке — с пояснением. Возвращает названия таких модулей.
|
||||
func markUnlicensed(states []release.ComponentState, allowed func(string) bool) []string {
|
||||
var disabled map[string]string
|
||||
if globalRC != nil {
|
||||
disabled = globalRC.Snapshot().Update.DisabledModules
|
||||
}
|
||||
var off []string
|
||||
for i := range states {
|
||||
c := &states[i]
|
||||
@@ -131,7 +185,7 @@ func markUnlicensed(states []release.ComponentState, allowed func(string) bool)
|
||||
continue
|
||||
}
|
||||
c.Update, c.New = false, false
|
||||
if c.Present {
|
||||
if _, already := disabled[c.Name]; c.Present && !already { // об отключённом уже сказано один раз
|
||||
c.Blocked = "не входит в лицензию — модуль отключён и не обновляется; чтобы подключить, свяжитесь с ZETIT"
|
||||
off = append(off, "«"+c.Title+"»")
|
||||
}
|
||||
@@ -163,6 +217,7 @@ func (u *Updater) saveCheckResult(st UpdateStatus, avail map[string]string) {
|
||||
cfg := u.rc.Snapshot().Update
|
||||
cfg.LastCheck = time.Now().UTC()
|
||||
cfg.LastResult = st.Message
|
||||
cfg.CheckFailed = avail == nil // nil — манифест получить не удалось
|
||||
cfg.Available = st.Available
|
||||
cfg.Notes = st.Notes
|
||||
if avail != nil {
|
||||
@@ -202,6 +257,7 @@ func (u *Updater) applyUpdate(ctx context.Context, names []string) (string, erro
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("манифест: %w", err)
|
||||
}
|
||||
syncSameFileVersions(m)
|
||||
plan := release.Plan("", m, release.LoadInstalled(release.InstalledFile), BuildVersion)
|
||||
release.MarkSplitModules(plan, u.allowed)
|
||||
off := markUnlicensed(plan, u.allowed)
|
||||
@@ -414,7 +470,10 @@ func updateComponentRows(rc *RuntimeConfig) ([]release.ComponentState, bool) {
|
||||
r.Update = r.Available != "" && (r.Present || r.Core) && (r.Installed == "" || release.IsNewer(r.Available, r.Installed))
|
||||
// Модуль, которого нет на сервере и нет в лицензии, не показываем: поставить его нельзя,
|
||||
// а в списке он выглядит так, будто входит в поставку.
|
||||
if r.Present || r.Core || (r.Available != "" && moduleAllowed(rc, r.Feature)) {
|
||||
// Модуль вне лицензии не показываем вовсе (и установленный: его служба отключена), пока
|
||||
// лицензия действует; без действующей лицензии список как есть.
|
||||
unlicensed := !r.Core && r.Feature != "" && r.Feature != "updates" && licenseStatus(rc).Valid && !moduleAllowed(rc, r.Feature)
|
||||
if !unlicensed && (r.Present || r.Core || (r.Available != "" && moduleAllowed(rc, r.Feature))) {
|
||||
out = append(out, r)
|
||||
}
|
||||
}
|
||||
@@ -422,7 +481,7 @@ func updateComponentRows(rc *RuntimeConfig) ([]release.ComponentState, bool) {
|
||||
markUnlicensed(out, func(f string) bool { return moduleAllowed(rc, f) })
|
||||
// Последняя проверка не удалась — предлагать установку по старым данным нельзя:
|
||||
// кнопка ушла бы в тот же отказ. Показываем причину и просим проверить заново.
|
||||
if updateResultFailed(set.LastResult) {
|
||||
if set.CheckFailed {
|
||||
for i := range out {
|
||||
if out[i].Update && out[i].Available != "" {
|
||||
out[i].Blocked = "проверка обновлений не удалась — нажмите «Проверить обновления»"
|
||||
@@ -476,6 +535,7 @@ func (u *Updater) updateLoop(ctx context.Context) {
|
||||
return
|
||||
case <-time.After(time.Minute):
|
||||
cleanupStaging(time.Hour)
|
||||
enforceModuleLicense(ctx, u.rc)
|
||||
u.ensureSplitModules(ctx)
|
||||
check()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user