From d5b07d76701e5d665e067e81387b053f312729b4 Mon Sep 17 00:00:00 2001 From: Dmitry Verkhoturov Date: Tue, 16 Dec 2025 18:11:47 +0100 Subject: [PATCH] Fix WriteHeader + RenderJSON causing wrong Content-Type header Replace WriteHeader() + RenderJSON() pattern with EncodeJSON() which properly sets Content-Type header before writing status code. The previous pattern caused Content-Type to default to text/plain instead of application/json, breaking frontend JSON parsing. Fixes #1979 --- backend/app/rest/api/migrator.go | 12 ++++-------- backend/app/rest/api/rest_private.go | 3 +-- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/backend/app/rest/api/migrator.go b/backend/app/rest/api/migrator.go index d519acae..32afb3b1 100644 --- a/backend/app/rest/api/migrator.go +++ b/backend/app/rest/api/migrator.go @@ -59,8 +59,7 @@ func (m *Migrator) importCtrl(w http.ResponseWriter, r *http.Request) { go m.runImport(siteID, r.URL.Query().Get("provider"), tmpfile) // import runs in background and sets busy flag for site - w.WriteHeader(http.StatusAccepted) - R.RenderJSON(w, R.JSON{"status": "import request accepted"}) + _ = R.EncodeJSON(w, http.StatusAccepted, R.JSON{"status": "import request accepted"}) } // POST /import/form?secret=key&site=site-id&provider=disqus|remark|wordpress @@ -94,8 +93,7 @@ func (m *Migrator) importFormCtrl(w http.ResponseWriter, r *http.Request) { go m.runImport(siteID, r.URL.Query().Get("provider"), tmpfile) // import runs in background and sets busy flag for site - w.WriteHeader(http.StatusAccepted) - R.RenderJSON(w, R.JSON{"status": "import request accepted"}) + _ = R.EncodeJSON(w, http.StatusAccepted, R.JSON{"status": "import request accepted"}) } // GET /wait?site=site-id @@ -115,8 +113,7 @@ func (m *Migrator) waitCtrl(w http.ResponseWriter, r *http.Request) { select { case <-ctx.Done(): - w.WriteHeader(http.StatusGatewayTimeout) - R.RenderJSON(w, R.JSON{"status": "timeout expired", "site_id": siteID}) + _ = R.EncodeJSON(w, http.StatusGatewayTimeout, R.JSON{"status": "timeout expired", "site_id": siteID}) return case <-time.After(100 * time.Millisecond): } @@ -210,8 +207,7 @@ func (m *Migrator) remapCtrl(w http.ResponseWriter, r *http.Request) { log.Printf("[DEBUG] convert request completed. site=%s, comments=%d", siteID, size) }() - w.WriteHeader(http.StatusAccepted) - R.RenderJSON(w, R.JSON{"status": "convert request accepted"}) + _ = R.EncodeJSON(w, http.StatusAccepted, R.JSON{"status": "convert request accepted"}) } // runImport reads from tmpfile and import for given siteID and provider diff --git a/backend/app/rest/api/rest_private.go b/backend/app/rest/api/rest_private.go index 35788b42..b709d3ce 100644 --- a/backend/app/rest/api/rest_private.go +++ b/backend/app/rest/api/rest_private.go @@ -174,8 +174,7 @@ func (s *private) createCommentCtrl(w http.ResponseWriter, r *http.Request) { log.Printf("[DEBUG] created comment %+v", finalComment) - w.WriteHeader(http.StatusCreated) - R.RenderJSON(w, &finalComment) + _ = R.EncodeJSON(w, http.StatusCreated, &finalComment) } // PUT /comment/{id}?site=siteID&url=post-url - update comment