From e0fdfcde063259268e6ef5e8b5acdef358330813 Mon Sep 17 00:00:00 2001 From: byrsapty Date: Fri, 28 Aug 2026 13:53:07 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9A=D0=B0=D0=BD=D0=B0=D0=BB=D0=B8:=20=D1=80?= =?UTF-8?q?=D0=B5=D0=B4=D0=B0=D0=B3=D1=83=D0=B2=D0=B0=D0=BD=D0=BD=D1=8F=20?= =?UTF-8?q?=D0=BD=D0=B5=20=D0=BF=D1=80=D0=B0=D1=86=D1=8E=D0=B2=D0=B0=D0=BB?= =?UTF-8?q?=D0=BE=20=D0=BD=D1=96=D0=BA=D0=BE=D0=BB=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Маршрут PUT /api/v1/channels/{id} стояв на handleCreateChannel з першого дня. Обробник читав тіло й ЗАВЖДИ кликав CreateChannel: він жодного разу не читав id зі шляху. При цьому UpdateChannel у store написана повністю й ретельно прокоментована — включно з рішенням «порожній секрет означає лишити токен як є, бо розшифрувати збережений заради показу означало б віддати його туди, звідки він не повернеться». У всьому дереві її не кликав НІХТО. Ззовні це виглядало так: правка каналу або заводила дубль, або — якщо назву лишили — падала з «канал із такою назвою вже є». Тобто відредагувати канал було неможливо взагалі, а повідомлення про помилку вказувало не на ту причину. Візерунок узято з правил у тому самому файлі: там гілка оновлення за PathValue уже стояла й працювала. --- server/internal/httpapi/alerts.go | 41 +++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/server/internal/httpapi/alerts.go b/server/internal/httpapi/alerts.go index 2076646..4313ffd 100644 --- a/server/internal/httpapi/alerts.go +++ b/server/internal/httpapi/alerts.go @@ -471,7 +471,7 @@ func (s *Server) handleCreateChannel(w http.ResponseWriter, r *http.Request, p * return } - id, err := s.store.CreateChannel(r.Context(), p.TenantID, store.ChannelInput{ + in := store.ChannelInput{ Kind: req.Kind, Name: req.Name, Config: jsonOr(req.Config, "{}"), @@ -479,7 +479,44 @@ func (s *Server) handleCreateChannel(w http.ResponseWriter, r *http.Request, p * MinSeverity: req.MinSeverity, Secret: req.Secret, Enabled: req.Enabled == nil || *req.Enabled, - }, s.keyring) + } + + // Оновлення наявного каналу приходить сюди ж — так само, як у + // правил вище: форма однакова, і розділяти її на дві гілки на + // клієнті означало б розійтися в перевірках. + // + // Цієї гілки НЕ БУЛО ЗОВСІМ. Маршрут PUT /channels/{id} стояв на + // цьому обробнику з першого дня, обробник читав тіло й завжди кликав + // CreateChannel, а написана й ретельно прокоментована UpdateChannel + // не мала жодного виклику в усьому дереві. + // + // Ззовні це виглядало так: правка каналу або заводила ДУБЛЬ, або — + // якщо назву лишили — падала з «канал із такою назвою вже є». Тобто + // відредагувати канал було неможливо взагалі, і повідомлення про + // помилку вказувало не на ту причину. + if id := r.PathValue("id"); id != "" { + if err := s.store.UpdateChannel(r.Context(), p.TenantID, id, in, s.keyring); err != nil { + if errors.Is(err, store.ErrNotFound) { + writeError(w, http.StatusNotFound, "not_found", "канал не знайдено") + return + } + if isUniqueViolation(err) { + writeError(w, http.StatusConflict, "duplicate", "канал із такою назвою вже є") + return + } + if isBadUUID(err) { + writeError(w, http.StatusNotFound, "not_found", "канал не знайдено") + return + } + s.log.Error("оновлення каналу", "err", err) + writeError(w, http.StatusInternalServerError, "internal", "внутрішня помилка") + return + } + writeJSON(w, http.StatusOK, map[string]any{"id": id}) + return + } + + id, err := s.store.CreateChannel(r.Context(), p.TenantID, in, s.keyring) if err != nil { if isUniqueViolation(err) { writeError(w, http.StatusConflict, "duplicate", "канал із такою назвою вже є")