Первичная настройка, часть 1: префикс торгового счёта DEPO — настройка с подсказкой по учёту (был зашит «ВТНКЦ» в 7 запросах); шаг 8 — адрес ИШ вместо ONYX, профили prod-gost/prod-rsa (пункт prod уводил bj в имитацию), без неиспользуемого контейнера, перезапуск bj после смены контура; реквизиты НРД на шаге 4; точная проверка процедур bj2depo и таблиц Fancy M2M; QUIK — объекты M2M и справочник получателей, без зашитого IP; сертификаты из справочника ИШ (FindCertificates — заглушка), уведомления о сроке за 30/14/7/3/1 день; тексты мастера, нумерация, режим разработки; уникальные id оповещений с русскими заголовками
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
This commit is contained in:
+38
-21
@@ -21,6 +21,7 @@ import (
|
||||
"github.com/jackc/pgx/v5"
|
||||
|
||||
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/cryptocli"
|
||||
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/nsdadapter"
|
||||
)
|
||||
|
||||
// crypto-сертификаты на текущих токенах (для отображения на странице).
|
||||
@@ -28,20 +29,7 @@ import (
|
||||
// Валидаты — здесь временно возвращаем пустой список, пока
|
||||
// crypto-service не получит метод ListCertificates.
|
||||
func (h *setupHandlers) listCertsForUI() []cryptocli.Certificate {
|
||||
s := h.rc.Snapshot()
|
||||
if s.Crypto.Provider == "" || s.Crypto.Provider == "stub" {
|
||||
return nil
|
||||
}
|
||||
cli := cryptocli.New(cryptocli.Config{
|
||||
Provider: cryptocli.Provider(s.Crypto.Provider),
|
||||
SocketPath: s.Crypto.SocketPath,
|
||||
ModulePath: s.Crypto.ModulePath,
|
||||
})
|
||||
defer cli.Close()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
certs, _ := cli.FindCertificates(ctx)
|
||||
return certs
|
||||
return storeCertsForUI(h.rc.Snapshot())
|
||||
}
|
||||
|
||||
// setupHandlers — обработчики /admin/setup/*.
|
||||
@@ -84,6 +72,7 @@ func registerSetup(mux *http.ServeMux, a *admin, rc *RuntimeConfig, svc *Service
|
||||
mux.HandleFunc("/admin/setup/wizard-depo/tables", h.wizardDepoTables)
|
||||
mux.HandleFunc("/admin/setup/wizard-depo/job", h.wizardDepoJob)
|
||||
mux.HandleFunc("/admin/setup/wizard-depo/recheck", h.wizardDepoRecheck)
|
||||
mux.HandleFunc("/admin/setup/fancy/acc-prefix", h.accPrefix)
|
||||
mux.HandleFunc("/admin/setup/license", h.saveLicense)
|
||||
mux.HandleFunc("/admin/setup/license/activate", h.licenseActivate)
|
||||
mux.HandleFunc("/admin/setup/license/renew", h.licenseRenewNow)
|
||||
@@ -91,6 +80,7 @@ func registerSetup(mux *http.ServeMux, a *admin, rc *RuntimeConfig, svc *Service
|
||||
mux.HandleFunc("/admin/setup/logo/reset", h.resetLogo)
|
||||
mux.HandleFunc("/admin/setup/nsd", h.saveNSD)
|
||||
mux.HandleFunc("/admin/setup/org", h.saveOrg)
|
||||
mux.HandleFunc("/admin/setup/nsd-requisites", h.saveNSDRequisites)
|
||||
mux.HandleFunc("/admin/setup/lk", h.saveLK)
|
||||
mux.HandleFunc("/admin/setup/lk/dist", h.lkSaveDist)
|
||||
mux.HandleFunc("/admin/setup/lk/dist.tar.gz", h.lkDistDownload)
|
||||
@@ -251,8 +241,8 @@ func (h *setupHandlers) renderWizard(w http.ResponseWriter, r *http.Request) {
|
||||
// Валидата ставится с образа/установщиком: если она на месте, шаг выполнен без ввода параметров
|
||||
// (bj работает с ней через свой криптосервис, путь к модулю не нужен).
|
||||
d.Done.Crypto = d.ValidataVer != "" || (s.Crypto.Provider != "" && s.Crypto.Provider != "stub" && s.Crypto.ModulePath != "")
|
||||
d.Done.Certs = len(d.Certs) > 0
|
||||
d.Done.NSD = s.NSD.IGWBaseURL != "" && s.NSD.Profile != ""
|
||||
d.Done.Certs = len(ownValidCerts(d.Certs)) > 0
|
||||
d.Done.NSD = doneNSD(s)
|
||||
d.Done.TestRun = s.LastTest != nil || (!d.DevMode && d.Done.NSD) // вне режима разработки тестовой заявки нет
|
||||
|
||||
// Мастер идёт по порядку: каждый шаг показывается, даже если он уже выполнен (установщиком,
|
||||
@@ -272,7 +262,7 @@ func (h *setupHandlers) renderWizard(w http.ResponseWriter, r *http.Request) {
|
||||
step = wizardSteps
|
||||
}
|
||||
d.Step = step
|
||||
if step == 4 && strings.TrimSpace(s.Fancy.Server) != "" && (orgCode(s) == "" || orgINN(s) == "") && depoDetectResult() == nil {
|
||||
if step == 4 && strings.TrimSpace(s.Fancy.Server) != "" && !doneOrg(s) && depoDetectResult() == nil {
|
||||
// организация ещё не задана, а база DEPO подключена — определяем реквизиты сами: карточка
|
||||
// покажет их заполненными, пользователю остаётся проверить и сохранить
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second)
|
||||
@@ -296,8 +286,8 @@ func (h *setupHandlers) renderWizard(w http.ResponseWriter, r *http.Request) {
|
||||
d.Sub = "done"
|
||||
}
|
||||
}
|
||||
d.Done.Org = orgCode(s) != "" && orgINN(s) != ""
|
||||
d.Done.Quik = !d.DepoLicensed || s.Quik.Verified
|
||||
d.Done.Org = doneOrg(s)
|
||||
d.Done.Quik = !d.DepoLicensed || doneQuik(s)
|
||||
if step == 3 && d.DepoLicensed && strings.TrimSpace(s.Fancy.Server) != "" {
|
||||
// Есть ли у сервера SQL Agent: у Express его нет, у PostgreSQL — тоже; тогда обработку
|
||||
// запускает сам bj. Иначе по умолчанию джобу SQL Agent создаёт bj.
|
||||
@@ -721,6 +711,22 @@ func (h *setupHandlers) saveNSD(w http.ResponseWriter, r *http.Request) {
|
||||
SectionID: strings.TrimSpace(r.FormValue("section_id")),
|
||||
IGWv904: r.FormValue("igw_v904") == "on",
|
||||
}
|
||||
cur := h.rc.Snapshot().NSD
|
||||
if r.FormValue("keep_requisites") == "1" { // мастер: реквизиты НРД — на шаге «Организация»
|
||||
s.DeponentCode, s.AccountID, s.SectionID = cur.DeponentCode, cur.AccountID, cur.SectionID
|
||||
}
|
||||
if _, ok := r.Form["key_container"]; !ok {
|
||||
s.KeyContainer = cur.KeyContainer
|
||||
}
|
||||
if s.Profile == "prod" { // старый пункт мастера: такого профиля нет — bj ушёл бы в имитацию
|
||||
s.Profile = "prod-gost"
|
||||
}
|
||||
if s.Profile != "" {
|
||||
if _, err := nsdadapter.LookupProfile(s.Profile); err != nil {
|
||||
setupFlash(w, r, "НРД: неизвестный контур "+s.Profile)
|
||||
return
|
||||
}
|
||||
}
|
||||
if s.IGWBaseURL != "" {
|
||||
if _, err := url.ParseRequestURI(s.IGWBaseURL); err != nil {
|
||||
setupFlash(w, r, "nsd: невалидный URL: "+err.Error())
|
||||
@@ -728,7 +734,7 @@ func (h *setupHandlers) saveNSD(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
// У ИШ нет /healthz — проверяем рабочий эндпоинт Web API.
|
||||
if err := tryHTTPHealth(s.IGWBaseURL + "/api/admin/engine/state"); err != nil {
|
||||
setupFlash(w, r, "nsd: ИШ не отвечает (engine/state): "+err.Error())
|
||||
setupFlash(w, r, "Шлюз НРД не отвечает по адресу "+s.IGWBaseURL+": "+err.Error()+". Проверьте, что ИШ установлен и запущен (служба igate).")
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -736,7 +742,18 @@ func (h *setupHandlers) saveNSD(w http.ResponseWriter, r *http.Request) {
|
||||
setupFlash(w, r, "nsd: ошибка сохранения: "+err.Error())
|
||||
return
|
||||
}
|
||||
setupFlash(w, r, "nsd-adapter: настройки сохранены")
|
||||
msg := "Шлюз НРД: настройки сохранены"
|
||||
if s.Profile != cur.Profile || s.IGWBaseURL != cur.IGWBaseURL {
|
||||
// отправитель в НРД создаётся при запуске — перезапускаем bj, чтобы контур и адрес применились сразу
|
||||
msg += ". bj перезапускается, чтобы применить контур и адрес, — обновите страницу через 10–15 секунд."
|
||||
go func() {
|
||||
time.Sleep(2 * time.Second)
|
||||
if _, err := restartUnit(context.Background(), "bj-server"); err != nil {
|
||||
log.Printf("setup: перезапуск bj-server после смены контура НРД: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
setupFlash(w, r, msg)
|
||||
}
|
||||
|
||||
func (h *setupHandlers) saveLK(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
Reference in New Issue
Block a user