Экспорт QUIK — отдельная служба bj-quikexport; модули, выделенные из ядра, ставятся с обновлением

bj-quikexport (modules/quikexport + cmd/bj-quikexport, 127.0.0.1:8084): управление ARQA
quikexport перенесено из bj-server. Настройки у службы в /var/lib/bj/.bj/quik-export/settings.json
(при первом запуске переносятся из setup.json), раздел отдаётся фрагментом с индикатором меню,
действия отвечают X-BJ-Flash. bj-server встраивает раздел в «Настройку», проксирует формы
/admin/setup/quikexport/… и передаёт лицензию (X-BJ-Licensed). Общая проверка служебного
ключа — modules/modkit (им пользуется и bj-npr), общее для прокси — lkgateway/modules.go.

Выделенные из ядра модули (bj-npr, bj-quikexport: Component.FromCore) при обновлении ставятся и
там, где их ещё нет, если входят в лицензию: release.MarkSplitModules, request.json "new",
root-установщик ставит и включает unit-файл из комплекта. Если в обновлении меняется сам
установщик, модули ставятся после перезапуска bj-server (ensureSplitModules).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
zuevav
2026-09-16 01:15:27 +03:00
parent 5527a464ca
commit 97e99b72c7
26 changed files with 1095 additions and 303 deletions
+3 -20
View File
@@ -11,7 +11,6 @@ package npr
import (
"bytes"
"context"
"crypto/subtle"
"embed"
"encoding/csv"
"encoding/json"
@@ -29,6 +28,8 @@ import (
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
"git.zetit.ru/zuevav/Bridge-and-Join-s/modules/modkit"
)
//go:embed web/*.html
@@ -148,24 +149,6 @@ func (s *Server) db() (*pgxpool.Pool, error) {
return p, nil
}
// requireKey — принимать запросы только от bj-server (кроме /healthz).
func (s *Server) requireKey(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/healthz" {
next.ServeHTTP(w, r)
return
}
key, err := os.ReadFile(s.cfg.KeyPath)
want := strings.TrimSpace(string(key))
got := r.Header.Get("X-BJ-Module-Key")
if err != nil || want == "" || subtle.ConstantTimeCompare([]byte(want), []byte(got)) != 1 {
http.Error(w, "модуль принимает запросы только от bj-server", http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
})
}
// page — полная страница фрагментом для макета bj-server.
func (s *Server) page(w http.ResponseWriter, t *template.Template, name, title string, data any) {
var buf bytes.Buffer
@@ -597,5 +580,5 @@ func (s *Server) Handler() http.Handler {
[]period{{1, "за 1 час"}, {6, "за 6 часов"}, {24, "за сутки"}, {168, "за 7 дней"}, {720, "за 30 дней"}}}
s.page(w, s.client, "npr_client", "Клиент "+code+" · НПР / УДС", data)
})
return s.requireKey(mux)
return modkit.RequireKey(s.cfg.KeyPath, mux)
}