Мониторинг связи с НРД: участки Шлюз/НРД/M2M/Отправка, сквозная проверка справочником раз в 10 мин, история за сутки на «Статусе», цепочка в шапке (30 с), автоповтор недоставленных до 24 ч, баннер и статус «ожидает отправки» в кабинете клиента

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
This commit is contained in:
zuevav
2026-09-25 16:17:25 +03:00
parent a0f6ce9f7b
commit 4cbefa41d5
18 changed files with 1075 additions and 62 deletions
+18 -3
View File
@@ -52,6 +52,7 @@ var templateFuncs = template.FuncMap{
"curUser": authTemplateFuncs["curUser"],
"can": authTemplateFuncs["can"],
"sendFailText": sendFailText,
"nsdSegTip": nsdSegTip,
"devOn": authTemplateFuncs["devOn"],
"devLicensed": authTemplateFuncs["devLicensed"],
"mod": func(f string) bool { return globalRC == nil || moduleAllowed(globalRC, f) }, // модуль в лицензии
@@ -361,8 +362,9 @@ type page struct {
NPR bool // модуль «НПР / УДС» включён и разрешён лицензией
IGWDown string // ИШ НРД не отвечает: «N мин» (пусто — отвечает или не проверялся)
IGWDetail string
NSDDown string // ИШ отвечает, но НРД не отвечает шлюзу (nsdlinkwatch.go)
DevMode bool // режим разработки (сервер лицензий): видны служебные разделы
NSDDown string // ИШ отвечает, но НРД не отвечает шлюзу (nsdlinkwatch.go)
NSDLink NSDHealth // цепочка «Шлюз · НРД · M2M · Отправка» для шапки (nsdhealth.go)
DevMode bool // режим разработки (сервер лицензий): видны служебные разделы
}
// globalRC — ссылка на runtime-конфиг для template-funcs/page helpers.
@@ -417,6 +419,10 @@ type statusData struct {
page
Checks []Status
CheckedAt time.Time
Strip []NSDStripRow // связь с НРД за сутки (nsdhealth.go)
StripFrom string // начало полосы
Fails []SendFailView // недоставленные переводы
Probe bool // только что запрошена проверка связи
}
// RegisterAdmin вешает HTML-маршруты /admin/* на mux. Возвращает admin
@@ -786,8 +792,16 @@ func (a *admin) renderHandbook(w http.ResponseWriter, _ *http.Request) {
func (a *admin) renderStatus(w http.ResponseWriter, r *http.Request, opts CheckOptions) {
s := CheckAll(r.Context(), opts)
pg := nowPage("Статус", "status")
var titles []string
for _, sg := range pg.NSDLink.Segs {
titles = append(titles, sg.Title)
}
now := time.Now()
render(w, a.status, statusData{
page: nowPage("Статус", "status"), Checks: s.Checks, CheckedAt: s.CheckedAt,
page: pg, Checks: s.Checks, CheckedAt: s.CheckedAt,
Strip: nsdStrip(titles, now), StripFrom: now.Add(-24 * time.Hour).Format("02.01 15:04"),
Fails: sendFailViews(), Probe: r.URL.Query().Get("probe") != "",
})
}
@@ -805,6 +819,7 @@ func nowPage(title, active string) page {
p := page{Title: title, Active: active, Now: time.Now().Format("02.01.2006 15:04:05")}
if globalRC != nil {
p.NPR = nprAvailable(globalRC)
p.NSDLink = nsdHealthNow(globalRC, time.Now())
p.DevMode = devMode(globalRC)
if down, d, det := igwStatus(); down {
p.IGWDown, p.IGWDetail = humanDur(d), det