Оповещения: группа «Администратор системы», блок «Кому писать» открыт; копии: адрес хранилища как в проводнике

Администратору (адрес из «Почты») — сбои (ИШ, связь с НРД, службы bj, DEPO) и обновления (новый выпуск bj,
ИШ, лицензия, сертификаты); отделам — переводы и поручения. Существующие установки: администратор получает
всё, что получал. Новые оповещения: «Доступно обновление bj» (раз на выпуск), «Служба … не работает /
снова работает» (модули и служба ключей Валидаты). Резервные копии: \\сервер\папка или сервер + общая
папка по полям → //сервер/папка; понятная причина отказа доступа.

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-10-06 22:44:35 +03:00
parent f07bb53162
commit d23ddf6e0c
16 changed files with 547 additions and 63 deletions
+43 -15
View File
@@ -1041,7 +1041,7 @@ func (h *setupHandlers) saveAlertsSMTP(w http.ResponseWriter, r *http.Request) {
Username: strings.TrimSpace(r.FormValue("username")),
Password: pickStr(r.FormValue("password"), a.SMTP.Password), // пароль в страницу не выводится: пусто — прежний
From: strings.TrimSpace(r.FormValue("from")),
To: strings.TrimSpace(r.FormValue("to")),
To: a.SMTP.To, // адрес администратора системы — в «Кому писать» (notifyGroupsCard)
LastMsg: a.SMTP.LastMsg,
LastAt: a.SMTP.LastAt,
}
@@ -1058,8 +1058,22 @@ func (h *setupHandlers) testAlertsSMTP(w http.ResponseWriter, r *http.Request) {
http.Error(w, "method", http.StatusMethodNotAllowed)
return
}
a := h.rc.Snapshot().Alerts
err := sendSMTP(a.SMTP, "[bj] Проверка уведомлений", "Это проверочное письмо от bj-server. Если вы его получили — SMTP-канал оповещений работает.")
cfg := h.rc.Snapshot()
a := cfg.Alerts
// кому: администратор системы, а пока его адреса нет — все адреса отделов из «Кому писать»
test := a.SMTP
if strings.TrimSpace(test.To) == "" {
var all []string
for _, k := range notifyKinds {
all = append(all, cfg.Incoming.Recipients(k.Key))
}
test.To = mergeEmails(all...)
}
if strings.TrimSpace(test.To) == "" {
setupFlash(w, r, "Проверка SMTP: укажите хотя бы один адрес в блоке «Кому писать» и сохраните")
return
}
err := sendSMTP(test, "[bj] Проверка уведомлений", "Это проверочное письмо от bj-server. Если вы его получили — SMTP-канал оповещений работает.")
a.SMTP.LastAt = time.Now()
if err != nil {
a.SMTP.LastMsg = "ошибка: " + err.Error()
@@ -1067,9 +1081,9 @@ func (h *setupHandlers) testAlertsSMTP(w http.ResponseWriter, r *http.Request) {
setupFlash(w, r, "Проверка SMTP: "+err.Error())
return
}
a.SMTP.LastMsg = "проверочное письмо отправлено"
a.SMTP.LastMsg = "проверочное письмо отправлено на " + test.To
_ = h.rc.UpdateAlerts(a)
setupFlash(w, r, "Проверка SMTP: письмо отправлено на "+a.SMTP.To)
setupFlash(w, r, "Проверка SMTP: письмо отправлено на "+test.To)
}
// saveAlertsZabbix сохраняет настройки Zabbix-хука.
@@ -1121,19 +1135,24 @@ func (h *setupHandlers) saveIncomingNotify(w http.ResponseWriter, r *http.Reques
http.Error(w, "method", http.StatusMethodNotAllowed)
return
}
// какие уведомления кому — только таблица: прежняя галочка «уведомления о переводах» — строка «Входящие»
s := IncomingNotifySettings{
Enabled: r.FormValue("enabled") == "on",
Enabled: true,
Emails: strings.TrimSpace(r.FormValue("emails")),
EmailsBack: strings.TrimSpace(r.FormValue("emails_back")),
EmailsMiddle: strings.TrimSpace(r.FormValue("emails_middle")),
Routes: map[string]string{},
RoutesV: 2,
}
prev := h.rc.Snapshot().Incoming.Routes
if prev == nil {
prev = defaultNotifyRoutes
}
cur := h.rc.Snapshot()
for _, k := range notifyKinds { // скрытые в форме типы (модуль не в лицензии) — прежний маршрут
s.Routes[k.Key] = prev[k.Key]
var gs []string
for _, g := range notifyGroups {
if cur.Incoming.RouteHas(k.Key, g.Key) {
gs = append(gs, g.Key)
}
}
s.Routes[k.Key] = strings.Join(gs, ",")
}
for _, k := range visibleNotifyKinds(h.rc) {
var gs []string
@@ -1148,6 +1167,15 @@ func (h *setupHandlers) saveIncomingNotify(w http.ResponseWriter, r *http.Reques
setupFlash(w, r, "Уведомления: ошибка сохранения: "+err.Error())
return
}
// адрес администратора системы хранится в настройках почты (Alerts.SMTP.To) — им же писали всегда
if _, ok := r.Form["emails_admin"]; ok {
a := cur.Alerts
a.SMTP.To = strings.TrimSpace(r.FormValue("emails_admin"))
if err := h.rc.UpdateAlerts(a); err != nil {
setupFlash(w, r, "Уведомления: ошибка сохранения адреса администратора: "+err.Error())
return
}
}
setupFlash(w, r, "Адреса и маршруты уведомлений сохранены")
}
@@ -1159,8 +1187,8 @@ func (h *setupHandlers) testIncomingNotify(w http.ResponseWriter, r *http.Reques
return
}
cfg := h.rc.Snapshot()
if strings.TrimSpace(cfg.Incoming.Recipients("incoming")) == "" {
setupFlash(w, r, "Проверка: сначала укажите почту депозитария")
if strings.TrimSpace(notifyRecipients(cfg, "incoming")) == "" {
setupFlash(w, r, "Проверка: сначала укажите адреса и отметьте в таблице, кому идут «Входящие переводы»")
return
}
smtp := cfg.Alerts.SMTP
@@ -1174,12 +1202,12 @@ func (h *setupHandlers) testIncomingNotify(w http.ResponseWriter, r *http.Reques
ReceivedAt: time.Now(),
Securities: []IncomingSecurity{{SecurityCode: "RU0009029540", ISIN: "RU0009029540", Quantity: "10"}},
}
smtp.To = cfg.Incoming.Recipients("incoming")
smtp.To = notifyRecipients(cfg, "incoming")
if err := sendSMTP(smtp, "[ПРОВЕРКА] MOEX МОСТ (M2M): к вам переведены ценные бумаги — требуется учёт", buildIncomingEmailBody(sample)); err != nil {
setupFlash(w, r, "Проверка уведомления депозитарию: "+err.Error())
return
}
setupFlash(w, r, "Проверка: письмо-уведомление отправлено на "+cfg.Incoming.Recipients("incoming"))
setupFlash(w, r, "Проверка: письмо-уведомление отправлено на "+notifyRecipients(cfg, "incoming"))
}
// lkPostJSON — POST JSON к API ЛК с Basic; возвращает (код, тело).