Уведомления в Telegram
CI / test (push) Successful in 2m45s
Release / release (push) Successful in 1m17s

- второй канал рядом с ntfy, оба необязательные: токен бота, несколько chat id,
  тема форума, свой адрес Bot API
- успешные бэкапы приходят без звука, в сообщениях ссылка на карточку клиента
- тестовая отправка синхронная: в настройках виден результат по каждому каналу
- токен бота не попадает в тексты ошибок и логи

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mrleo1nidandClaude Opus 5.5 committed 2026-10-06 22:29:26 +03:00
1 parent a488d2ea08
commit 897ad48a2d
8 files changed
+354 -38

No files matched your search

+49 -9
View File
@@ -376,29 +376,69 @@ func (s *Server) webBackupDelete(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, fmt.Sprintf("/clients/%d", c.ID), http.StatusSeeOther)
}
func (s *Server) webSettings(w http.ResponseWriter, r *http.Request) {
s.render(w, r, "settings", map[string]any{
func (s *Server) settingsData(r *http.Request) map[string]any {
return map[string]any{
"NtfyURL": s.st.Setting(settingNtfyURL),
"NtfyAuthSet": s.st.Setting(settingNtfyAuth) != "",
"TGTokenSet": s.st.Setting(settingTGToken) != "",
"TGChats": s.st.Setting(settingTGChats),
"TGThread": s.st.Setting(settingTGThread),
"TGAPIURL": s.st.Setting(settingTGAPIURL),
"NotifySuccess": s.st.Setting(settingNotifySuccess) == "1",
"Saved": r.URL.Query().Get("saved") != "",
})
}
}
func (s *Server) webSettings(w http.ResponseWriter, r *http.Request) {
s.render(w, r, "settings", s.settingsData(r))
}
// setSecret обновляет секретную настройку: пустое поле — не менять, галочка clear — удалить.
func (s *Server) setSecret(r *http.Request, key, field string) {
if r.FormValue(field+"_clear") == "1" {
_ = s.st.SetSetting(key, "")
} else if v := strings.TrimSpace(r.FormValue(field)); v != "" {
_ = s.st.SetSetting(key, v)
}
}
type testResult struct {
Channel string
Err string
}
func (s *Server) webSettingsSave(w http.ResponseWriter, r *http.Request) {
_ = s.st.SetSetting(settingNtfyURL, strings.TrimSpace(r.FormValue("ntfy_url")))
if r.FormValue("ntfy_auth_clear") == "1" {
_ = s.st.SetSetting(settingNtfyAuth, "")
} else if v := strings.TrimSpace(r.FormValue("ntfy_auth")); v != "" {
_ = s.st.SetSetting(settingNtfyAuth, v)
}
s.setSecret(r, settingNtfyAuth, "ntfy_auth")
s.setSecret(r, settingTGToken, "tg_token")
_ = s.st.SetSetting(settingTGChats, strings.Join(splitList(r.FormValue("tg_chats")), ","))
_ = s.st.SetSetting(settingTGThread, strings.TrimSpace(r.FormValue("tg_thread")))
_ = s.st.SetSetting(settingTGAPIURL, strings.TrimSpace(r.FormValue("tg_api_url")))
ns := "0"
if r.FormValue("notify_success") == "1" {
ns = "1"
}
_ = s.st.SetSetting(settingNotifySuccess, ns)
if r.FormValue("test") == "1" {
s.notify("backup-master: тест", "Тестовое уведомление", "default", "test_tube")
res := s.notifySync(r.Context(), notification{
Title: "backup-master: тест", Msg: "Тестовое уведомление", Priority: "default", Tags: "test_tube", Link: s.cfg.PublicURL,
})
var results []testResult
for _, name := range []string{"ntfy", "telegram"} {
if err, ok := res[name]; ok {
tr := testResult{Channel: name}
if err != nil {
tr.Err = err.Error()
}
results = append(results, tr)
}
}
data := s.settingsData(r)
data["Saved"] = true
data["Tested"] = true
data["TestResults"] = results
s.render(w, r, "settings", data)
return
}
http.Redirect(w, r, "/settings?saved=1", http.StatusSeeOther)
}