소스 검색

refactor(http): move response format helpers into parent package

Frédéric Guillot 1 개월 전
부모
커밋
b18ecc0a78
100개의 변경된 파일1522개의 추가작업 그리고 1590개의 파일을 삭제
  1. 2 2
      internal/api/api.go
  2. 10 10
      internal/api/api_key.go
  3. 22 22
      internal/api/category.go
  4. 12 12
      internal/api/enclosure.go
  5. 54 54
      internal/api/entry.go
  6. 35 35
      internal/api/feed.go
  7. 7 7
      internal/api/icon.go
  8. 8 8
      internal/api/middleware.go
  9. 5 6
      internal/api/opml.go
  10. 6 6
      internal/api/subscription.go
  11. 38 38
      internal/api/user.go
  12. 24 24
      internal/fever/handler.go
  13. 4 4
      internal/fever/middleware.go
  14. 87 88
      internal/googlereader/handler.go
  15. 23 24
      internal/http/response/html.go
  16. 0 240
      internal/http/response/html/html_test.go
  17. 210 0
      internal/http/response/html_test.go
  18. 176 0
      internal/http/response/json.go
  19. 0 215
      internal/http/response/json/json.go
  20. 0 312
      internal/http/response/json/json_test.go
  21. 330 0
      internal/http/response/json_test.go
  22. 23 0
      internal/http/response/xml.go
  23. 0 27
      internal/http/response/xml/xml.go
  24. 16 25
      internal/http/response/xml_test.go
  25. 3 3
      internal/ui/about.go
  26. 3 3
      internal/ui/api_key_create.go
  27. 4 4
      internal/ui/api_key_list.go
  28. 3 3
      internal/ui/api_key_remove.go
  29. 5 5
      internal/ui/api_key_save.go
  30. 3 3
      internal/ui/category_create.go
  31. 5 5
      internal/ui/category_edit.go
  32. 7 7
      internal/ui/category_entries.go
  33. 7 7
      internal/ui/category_entries_all.go
  34. 7 7
      internal/ui/category_entries_starred.go
  35. 6 6
      internal/ui/category_feeds.go
  36. 4 4
      internal/ui/category_list.go
  37. 5 5
      internal/ui/category_mark_as_read.go
  38. 4 4
      internal/ui/category_refresh.go
  39. 6 6
      internal/ui/category_remove.go
  40. 4 4
      internal/ui/category_remove_feed.go
  41. 5 5
      internal/ui/category_save.go
  42. 7 7
      internal/ui/category_update.go
  43. 8 8
      internal/ui/entry_category.go
  44. 6 6
      internal/ui/entry_enclosure_save_position.go
  45. 8 8
      internal/ui/entry_feed.go
  46. 7 7
      internal/ui/entry_read.go
  47. 5 5
      internal/ui/entry_save.go
  48. 9 9
      internal/ui/entry_scraper.go
  49. 8 8
      internal/ui/entry_search.go
  50. 8 8
      internal/ui/entry_starred.go
  51. 8 8
      internal/ui/entry_tag.go
  52. 3 3
      internal/ui/entry_toggle_starred.go
  53. 9 9
      internal/ui/entry_unread.go
  54. 5 5
      internal/ui/entry_update_status.go
  55. 6 6
      internal/ui/feed_edit.go
  56. 7 7
      internal/ui/feed_entries.go
  57. 7 7
      internal/ui/feed_entries_all.go
  58. 2 3
      internal/ui/feed_icon.go
  59. 4 4
      internal/ui/feed_list.go
  60. 4 4
      internal/ui/feed_mark_as_read.go
  61. 4 4
      internal/ui/feed_refresh.go
  62. 4 4
      internal/ui/feed_remove.go
  63. 8 8
      internal/ui/feed_update.go
  64. 5 5
      internal/ui/history_entries.go
  65. 3 3
      internal/ui/history_flush.go
  66. 4 4
      internal/ui/integration_show.go
  67. 8 8
      internal/ui/integration_update.go
  68. 8 8
      internal/ui/login_check.go
  69. 4 4
      internal/ui/login_show.go
  70. 4 4
      internal/ui/logout.go
  71. 11 11
      internal/ui/middleware.go
  72. 16 16
      internal/ui/oauth2_callback.go
  73. 4 4
      internal/ui/oauth2_redirect.go
  74. 9 9
      internal/ui/oauth2_unlink.go
  75. 2 2
      internal/ui/offline.go
  76. 4 4
      internal/ui/opml_export.go
  77. 3 3
      internal/ui/opml_import.go
  78. 11 11
      internal/ui/opml_upload.go
  79. 11 11
      internal/ui/proxy.go
  80. 5 5
      internal/ui/search.go
  81. 4 4
      internal/ui/session_list.go
  82. 3 3
      internal/ui/session_remove.go
  83. 4 4
      internal/ui/settings_show.go
  84. 7 7
      internal/ui/settings_update.go
  85. 7 7
      internal/ui/share.go
  86. 5 5
      internal/ui/shared_entries.go
  87. 5 5
      internal/ui/starred_entries.go
  88. 8 8
      internal/ui/starred_entry_category.go
  89. 2 2
      internal/ui/static_app_icon.go
  90. 2 2
      internal/ui/static_favicon.go
  91. 2 2
      internal/ui/static_javascript.go
  92. 3 3
      internal/ui/static_manifest.go
  93. 2 2
      internal/ui/static_stylesheet.go
  94. 4 4
      internal/ui/subscription_add.go
  95. 4 4
      internal/ui/subscription_bookmarklet.go
  96. 6 6
      internal/ui/subscription_choose.go
  97. 11 11
      internal/ui/subscription_submit.go
  98. 6 6
      internal/ui/tag_entries_all.go
  99. 5 5
      internal/ui/unread_entries.go
  100. 10 10
      internal/ui/unread_entry_category.go

+ 2 - 2
internal/api/api.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 	"runtime"
 
-	"miniflux.app/v2/internal/http/response/json"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/storage"
 	"miniflux.app/v2/internal/version"
 	"miniflux.app/v2/internal/worker"
@@ -84,7 +84,7 @@ func Serve(router *mux.Router, store *storage.Storage, pool *worker.Pool) {
 }
 
 func (h *handler) versionHandler(w http.ResponseWriter, r *http.Request) {
-	json.OK(w, r, &versionResponse{
+	response.JSON(w, r, &versionResponse{
 		Version:   version.Version,
 		Commit:    version.Commit,
 		BuildDate: version.BuildDate,

+ 10 - 10
internal/api/api_key.go

@@ -9,7 +9,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/json"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/storage"
 	"miniflux.app/v2/internal/validator"
@@ -20,32 +20,32 @@ func (h *handler) createAPIKey(w http.ResponseWriter, r *http.Request) {
 
 	var apiKeyCreationRequest model.APIKeyCreationRequest
 	if err := json_parser.NewDecoder(r.Body).Decode(&apiKeyCreationRequest); err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
 	if validationErr := validator.ValidateAPIKeyCreation(h.store, userID, &apiKeyCreationRequest); validationErr != nil {
-		json.BadRequest(w, r, validationErr.Error())
+		response.JSONBadRequest(w, r, validationErr.Error())
 		return
 	}
 
 	apiKey, err := h.store.CreateAPIKey(userID, apiKeyCreationRequest.Description)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
-	json.Created(w, r, apiKey)
+	response.JSONCreated(w, r, apiKey)
 }
 
 func (h *handler) getAPIKeys(w http.ResponseWriter, r *http.Request) {
 	userID := request.UserID(r)
 	apiKeys, err := h.store.APIKeys(userID)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
-	json.OK(w, r, apiKeys)
+	response.JSON(w, r, apiKeys)
 }
 
 func (h *handler) deleteAPIKey(w http.ResponseWriter, r *http.Request) {
@@ -54,11 +54,11 @@ func (h *handler) deleteAPIKey(w http.ResponseWriter, r *http.Request) {
 
 	if err := h.store.DeleteAPIKey(userID, apiKeyID); err != nil {
 		if errors.Is(err, storage.ErrAPIKeyNotFound) {
-			json.NotFound(w, r)
+			response.JSONNotFound(w, r)
 			return
 		}
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
-	json.NoContent(w, r)
+	response.JSONNoContent(w, r)
 }

+ 22 - 22
internal/api/category.go

@@ -11,7 +11,7 @@ import (
 
 	"miniflux.app/v2/internal/config"
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/json"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/validator"
 )
@@ -21,22 +21,22 @@ func (h *handler) createCategory(w http.ResponseWriter, r *http.Request) {
 
 	var categoryCreationRequest model.CategoryCreationRequest
 	if err := json_parser.NewDecoder(r.Body).Decode(&categoryCreationRequest); err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
 	if validationErr := validator.ValidateCategoryCreation(h.store, userID, &categoryCreationRequest); validationErr != nil {
-		json.BadRequest(w, r, validationErr.Error())
+		response.JSONBadRequest(w, r, validationErr.Error())
 		return
 	}
 
 	category, err := h.store.CreateCategory(userID, &categoryCreationRequest)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
-	json.Created(w, r, category)
+	response.JSONCreated(w, r, category)
 }
 
 func (h *handler) updateCategory(w http.ResponseWriter, r *http.Request) {
@@ -45,34 +45,34 @@ func (h *handler) updateCategory(w http.ResponseWriter, r *http.Request) {
 
 	category, err := h.store.Category(userID, categoryID)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	if category == nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
 	var categoryModificationRequest model.CategoryModificationRequest
 	if err := json_parser.NewDecoder(r.Body).Decode(&categoryModificationRequest); err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
 	if validationErr := validator.ValidateCategoryModification(h.store, userID, category.ID, &categoryModificationRequest); validationErr != nil {
-		json.BadRequest(w, r, validationErr.Error())
+		response.JSONBadRequest(w, r, validationErr.Error())
 		return
 	}
 
 	categoryModificationRequest.Patch(category)
 
 	if err := h.store.UpdateCategory(category); err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
-	json.Created(w, r, category)
+	response.JSONCreated(w, r, category)
 }
 
 func (h *handler) markCategoryAsRead(w http.ResponseWriter, r *http.Request) {
@@ -81,21 +81,21 @@ func (h *handler) markCategoryAsRead(w http.ResponseWriter, r *http.Request) {
 
 	category, err := h.store.Category(userID, categoryID)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	if category == nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
 	if err = h.store.MarkCategoryAsRead(userID, categoryID, time.Now()); err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
-	json.NoContent(w, r)
+	response.JSONNoContent(w, r)
 }
 
 func (h *handler) getCategories(w http.ResponseWriter, r *http.Request) {
@@ -110,10 +110,10 @@ func (h *handler) getCategories(w http.ResponseWriter, r *http.Request) {
 	}
 
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
-	json.OK(w, r, categories)
+	response.JSON(w, r, categories)
 }
 
 func (h *handler) removeCategory(w http.ResponseWriter, r *http.Request) {
@@ -121,16 +121,16 @@ func (h *handler) removeCategory(w http.ResponseWriter, r *http.Request) {
 	categoryID := request.RouteInt64Param(r, "categoryID")
 
 	if !h.store.CategoryIDExists(userID, categoryID) {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
 	if err := h.store.RemoveCategory(userID, categoryID); err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
-	json.NoContent(w, r)
+	response.JSONNoContent(w, r)
 }
 
 func (h *handler) refreshCategory(w http.ResponseWriter, r *http.Request) {
@@ -147,7 +147,7 @@ func (h *handler) refreshCategory(w http.ResponseWriter, r *http.Request) {
 
 	jobs, err := batchBuilder.FetchJobs()
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
@@ -160,5 +160,5 @@ func (h *handler) refreshCategory(w http.ResponseWriter, r *http.Request) {
 
 	go h.pool.Push(jobs)
 
-	json.NoContent(w, r)
+	response.JSONNoContent(w, r)
 }

+ 12 - 12
internal/api/enclosure.go

@@ -9,7 +9,7 @@ import (
 
 	"miniflux.app/v2/internal/config"
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/json"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/validator"
 )
@@ -19,24 +19,24 @@ func (h *handler) getEnclosureByID(w http.ResponseWriter, r *http.Request) {
 
 	enclosure, err := h.store.GetEnclosure(enclosureID)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	if enclosure == nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
 	userID := request.UserID(r)
 	if enclosure.UserID != userID {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
 	enclosure.ProxifyEnclosureURL(h.router, config.Opts.MediaProxyMode(), config.Opts.MediaProxyResourceTypes())
 
-	json.OK(w, r, enclosure)
+	response.JSON(w, r, enclosure)
 }
 
 func (h *handler) updateEnclosureByID(w http.ResponseWriter, r *http.Request) {
@@ -44,37 +44,37 @@ func (h *handler) updateEnclosureByID(w http.ResponseWriter, r *http.Request) {
 
 	var enclosureUpdateRequest model.EnclosureUpdateRequest
 	if err := json_parser.NewDecoder(r.Body).Decode(&enclosureUpdateRequest); err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
 	if err := validator.ValidateEnclosureUpdateRequest(&enclosureUpdateRequest); err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
 	enclosure, err := h.store.GetEnclosure(enclosureID)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	if enclosure == nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
 	userID := request.UserID(r)
 	if enclosure.UserID != userID {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
 	enclosure.MediaProgression = enclosureUpdateRequest.MediaProgression
 	if err := h.store.UpdateEnclosure(enclosure); err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
-	json.NoContent(w, r)
+	response.JSONNoContent(w, r)
 }

+ 54 - 54
internal/api/entry.go

@@ -13,7 +13,7 @@ import (
 	"miniflux.app/v2/internal/config"
 	"miniflux.app/v2/internal/crypto"
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/json"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/integration"
 	"miniflux.app/v2/internal/mediaproxy"
 	"miniflux.app/v2/internal/model"
@@ -27,19 +27,19 @@ import (
 func (h *handler) getEntryFromBuilder(w http.ResponseWriter, r *http.Request, b *storage.EntryQueryBuilder) {
 	entry, err := b.GetEntry()
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	if entry == nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
 	entry.Content = mediaproxy.RewriteDocumentWithAbsoluteProxyURL(h.router, entry.Content)
 	entry.Enclosures.ProxifyEnclosureURL(h.router, config.Opts.MediaProxyMode(), config.Opts.MediaProxyResourceTypes())
 
-	json.OK(w, r, entry)
+	response.JSON(w, r, entry)
 }
 
 func (h *handler) getFeedEntry(w http.ResponseWriter, r *http.Request) {
@@ -93,40 +93,40 @@ func (h *handler) findEntries(w http.ResponseWriter, r *http.Request, feedID int
 	statuses := request.QueryStringParamList(r, "status")
 	for _, status := range statuses {
 		if err := validator.ValidateEntryStatus(status); err != nil {
-			json.BadRequest(w, r, err)
+			response.JSONBadRequest(w, r, err)
 			return
 		}
 	}
 
 	order := request.QueryStringParam(r, "order", model.DefaultSortingOrder)
 	if err := validator.ValidateEntryOrder(order); err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
 	direction := request.QueryStringParam(r, "direction", model.DefaultSortingDirection)
 	if err := validator.ValidateDirection(direction); err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
 	limit := request.QueryIntParam(r, "limit", 100)
 	offset := request.QueryIntParam(r, "offset", 0)
 	if err := validator.ValidateRange(offset, limit); err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
 	userID := request.UserID(r)
 	categoryID = request.QueryInt64Param(r, "category_id", categoryID)
 	if categoryID > 0 && !h.store.CategoryIDExists(userID, categoryID) {
-		json.BadRequest(w, r, errors.New("invalid category ID"))
+		response.JSONBadRequest(w, r, errors.New("invalid category ID"))
 		return
 	}
 
 	feedID = request.QueryInt64Param(r, "feed_id", feedID)
 	if feedID > 0 && !h.store.FeedExists(userID, feedID) {
-		json.BadRequest(w, r, errors.New("invalid feed ID"))
+		response.JSONBadRequest(w, r, errors.New("invalid feed ID"))
 		return
 	}
 
@@ -155,13 +155,13 @@ func (h *handler) findEntries(w http.ResponseWriter, r *http.Request, feedID int
 
 	entries, err := builder.GetEntries()
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	count, err := builder.CountEntries()
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
@@ -169,37 +169,37 @@ func (h *handler) findEntries(w http.ResponseWriter, r *http.Request, feedID int
 		entries[i].Content = mediaproxy.RewriteDocumentWithAbsoluteProxyURL(h.router, entries[i].Content)
 	}
 
-	json.OK(w, r, &entriesResponse{Total: count, Entries: entries})
+	response.JSON(w, r, &entriesResponse{Total: count, Entries: entries})
 }
 
 func (h *handler) setEntryStatus(w http.ResponseWriter, r *http.Request) {
 	var entriesStatusUpdateRequest model.EntriesStatusUpdateRequest
 	if err := json_parser.NewDecoder(r.Body).Decode(&entriesStatusUpdateRequest); err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
 	if err := validator.ValidateEntriesStatusUpdateRequest(&entriesStatusUpdateRequest); err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
 	if err := h.store.SetEntriesStatus(request.UserID(r), entriesStatusUpdateRequest.EntryIDs, entriesStatusUpdateRequest.Status); err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
-	json.NoContent(w, r)
+	response.JSONNoContent(w, r)
 }
 
 func (h *handler) toggleStarred(w http.ResponseWriter, r *http.Request) {
 	entryID := request.RouteInt64Param(r, "entryID")
 	if err := h.store.ToggleStarred(request.UserID(r), entryID); err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
-	json.NoContent(w, r)
+	response.JSONNoContent(w, r)
 }
 
 func (h *handler) saveEntry(w http.ResponseWriter, r *http.Request) {
@@ -209,41 +209,41 @@ func (h *handler) saveEntry(w http.ResponseWriter, r *http.Request) {
 	builder.WithoutStatus(model.EntryStatusRemoved)
 
 	if !h.store.HasSaveEntry(request.UserID(r)) {
-		json.BadRequest(w, r, errors.New("no third-party integration enabled"))
+		response.JSONBadRequest(w, r, errors.New("no third-party integration enabled"))
 		return
 	}
 
 	entry, err := builder.GetEntry()
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	if entry == nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
 	settings, err := h.store.Integration(request.UserID(r))
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	go integration.SendEntry(entry, settings)
 
-	json.Accepted(w, r)
+	response.JSONAccepted(w, r)
 }
 
 func (h *handler) updateEntry(w http.ResponseWriter, r *http.Request) {
 	var entryUpdateRequest model.EntryUpdateRequest
 	if err := json_parser.NewDecoder(r.Body).Decode(&entryUpdateRequest); err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
 	if err := validator.ValidateEntryModification(&entryUpdateRequest); err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
@@ -256,23 +256,23 @@ func (h *handler) updateEntry(w http.ResponseWriter, r *http.Request) {
 
 	entry, err := entryBuilder.GetEntry()
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	if entry == nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
 	user, err := h.store.UserByID(loggedUserID)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	if user == nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
@@ -287,11 +287,11 @@ func (h *handler) updateEntry(w http.ResponseWriter, r *http.Request) {
 	}
 
 	if err := h.store.UpdateEntryTitleAndContent(entry); err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
-	json.Created(w, r, entry)
+	response.JSONCreated(w, r, entry)
 }
 
 func (h *handler) importFeedEntry(w http.ResponseWriter, r *http.Request) {
@@ -299,23 +299,23 @@ func (h *handler) importFeedEntry(w http.ResponseWriter, r *http.Request) {
 	feedID := request.RouteInt64Param(r, "feedID")
 
 	if feedID <= 0 {
-		json.BadRequest(w, r, errors.New("invalid feed ID"))
+		response.JSONBadRequest(w, r, errors.New("invalid feed ID"))
 		return
 	}
 
 	if !h.store.FeedExists(userID, feedID) {
-		json.BadRequest(w, r, errors.New("feed does not exist"))
+		response.JSONBadRequest(w, r, errors.New("feed does not exist"))
 		return
 	}
 
 	var importRequest entryImportRequest
 	if err := json_parser.NewDecoder(r.Body).Decode(&importRequest); err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
 	if importRequest.URL == "" {
-		json.BadRequest(w, r, errors.New("url is required"))
+		response.JSONBadRequest(w, r, errors.New("url is required"))
 		return
 	}
 
@@ -324,7 +324,7 @@ func (h *handler) importFeedEntry(w http.ResponseWriter, r *http.Request) {
 	}
 
 	if err := validator.ValidateEntryStatus(importRequest.Status); err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
@@ -354,12 +354,12 @@ func (h *handler) importFeedEntry(w http.ResponseWriter, r *http.Request) {
 
 	user, err := h.store.UserByID(userID)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	if user == nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
@@ -373,28 +373,28 @@ func (h *handler) importFeedEntry(w http.ResponseWriter, r *http.Request) {
 
 	created, err := h.store.InsertEntryForFeed(userID, feedID, entry)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	if err := h.store.SetEntriesStatus(userID, []int64{entry.ID}, importRequest.Status); err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 	entry.Status = importRequest.Status
 
 	if importRequest.Starred {
 		if err := h.store.SetEntriesStarredState(userID, []int64{entry.ID}, true); err != nil {
-			json.ServerError(w, r, err)
+			response.JSONServerError(w, r, err)
 			return
 		}
 		entry.Starred = true
 	}
 
 	if created {
-		json.Created(w, r, entryIDResponse{ID: entry.ID})
+		response.JSONCreated(w, r, entryIDResponse{ID: entry.ID})
 	} else {
-		json.OK(w, r, entryIDResponse{ID: entry.ID})
+		response.JSON(w, r, entryIDResponse{ID: entry.ID})
 	}
 }
 
@@ -408,23 +408,23 @@ func (h *handler) fetchContent(w http.ResponseWriter, r *http.Request) {
 
 	entry, err := entryBuilder.GetEntry()
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	if entry == nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
 	user, err := h.store.UserByID(loggedUserID)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	if user == nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
@@ -432,35 +432,35 @@ func (h *handler) fetchContent(w http.ResponseWriter, r *http.Request) {
 	feedBuilder.WithFeedID(entry.FeedID)
 	feed, err := feedBuilder.GetFeed()
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	if feed == nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
 	if err := processor.ProcessEntryWebPage(feed, entry, user); err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	shouldUpdateContent := request.QueryBoolParam(r, "update_content", false)
 	if shouldUpdateContent {
 		if err := h.store.UpdateEntryTitleAndContent(entry); err != nil {
-			json.ServerError(w, r, err)
+			response.JSONServerError(w, r, err)
 			return
 		}
 	}
 
-	json.OK(w, r, entryContentResponse{Content: mediaproxy.RewriteDocumentWithAbsoluteProxyURL(h.router, entry.Content), ReadingTime: entry.ReadingTime})
+	response.JSON(w, r, entryContentResponse{Content: mediaproxy.RewriteDocumentWithAbsoluteProxyURL(h.router, entry.Content), ReadingTime: entry.ReadingTime})
 }
 
 func (h *handler) flushHistory(w http.ResponseWriter, r *http.Request) {
 	loggedUserID := request.UserID(r)
 	go h.store.FlushHistory(loggedUserID)
-	json.Accepted(w, r)
+	response.JSONAccepted(w, r)
 }
 
 func configureFilters(builder *storage.EntryQueryBuilder, r *http.Request) {

+ 35 - 35
internal/api/feed.go

@@ -11,7 +11,7 @@ import (
 
 	"miniflux.app/v2/internal/config"
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/json"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/model"
 	feedHandler "miniflux.app/v2/internal/reader/handler"
 	"miniflux.app/v2/internal/validator"
@@ -22,7 +22,7 @@ func (h *handler) createFeed(w http.ResponseWriter, r *http.Request) {
 
 	var feedCreationRequest model.FeedCreationRequest
 	if err := json_parser.NewDecoder(r.Body).Decode(&feedCreationRequest); err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
@@ -30,24 +30,24 @@ func (h *handler) createFeed(w http.ResponseWriter, r *http.Request) {
 	if feedCreationRequest.CategoryID == 0 {
 		category, err := h.store.FirstCategory(userID)
 		if err != nil {
-			json.ServerError(w, r, err)
+			response.JSONServerError(w, r, err)
 			return
 		}
 		feedCreationRequest.CategoryID = category.ID
 	}
 
 	if validationErr := validator.ValidateFeedCreation(h.store, userID, &feedCreationRequest); validationErr != nil {
-		json.BadRequest(w, r, validationErr.Error())
+		response.JSONBadRequest(w, r, validationErr.Error())
 		return
 	}
 
 	feed, localizedError := feedHandler.CreateFeed(h.store, userID, &feedCreationRequest)
 	if localizedError != nil {
-		json.ServerError(w, r, localizedError.Error())
+		response.JSONServerError(w, r, localizedError.Error())
 		return
 	}
 
-	json.Created(w, r, &feedCreationResponse{FeedID: feed.ID})
+	response.JSONCreated(w, r, &feedCreationResponse{FeedID: feed.ID})
 }
 
 func (h *handler) refreshFeed(w http.ResponseWriter, r *http.Request) {
@@ -55,17 +55,17 @@ func (h *handler) refreshFeed(w http.ResponseWriter, r *http.Request) {
 	userID := request.UserID(r)
 
 	if !h.store.FeedExists(userID, feedID) {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
 	localizedError := feedHandler.RefreshFeed(h.store, userID, feedID, false)
 	if localizedError != nil {
-		json.ServerError(w, r, localizedError.Error())
+		response.JSONServerError(w, r, localizedError.Error())
 		return
 	}
 
-	json.NoContent(w, r)
+	response.JSONNoContent(w, r)
 }
 
 func (h *handler) refreshAllFeeds(w http.ResponseWriter, r *http.Request) {
@@ -80,7 +80,7 @@ func (h *handler) refreshAllFeeds(w http.ResponseWriter, r *http.Request) {
 
 	jobs, err := batchBuilder.FetchJobs()
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
@@ -92,13 +92,13 @@ func (h *handler) refreshAllFeeds(w http.ResponseWriter, r *http.Request) {
 
 	go h.pool.Push(jobs)
 
-	json.NoContent(w, r)
+	response.JSONNoContent(w, r)
 }
 
 func (h *handler) updateFeed(w http.ResponseWriter, r *http.Request) {
 	var feedModificationRequest model.FeedModificationRequest
 	if err := json_parser.NewDecoder(r.Body).Decode(&feedModificationRequest); err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
@@ -107,34 +107,34 @@ func (h *handler) updateFeed(w http.ResponseWriter, r *http.Request) {
 
 	originalFeed, err := h.store.FeedByID(userID, feedID)
 	if err != nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
 	if originalFeed == nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
 	if validationErr := validator.ValidateFeedModification(h.store, userID, originalFeed.ID, &feedModificationRequest); validationErr != nil {
-		json.BadRequest(w, r, validationErr.Error())
+		response.JSONBadRequest(w, r, validationErr.Error())
 		return
 	}
 
 	feedModificationRequest.Patch(originalFeed)
 	originalFeed.ResetErrorCounter()
 	if err := h.store.UpdateFeed(originalFeed); err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	originalFeed, err = h.store.FeedByID(userID, feedID)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
-	json.Created(w, r, originalFeed)
+	response.JSONCreated(w, r, originalFeed)
 }
 
 func (h *handler) markFeedAsRead(w http.ResponseWriter, r *http.Request) {
@@ -142,16 +142,16 @@ func (h *handler) markFeedAsRead(w http.ResponseWriter, r *http.Request) {
 	userID := request.UserID(r)
 
 	if !h.store.FeedExists(userID, feedID) {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
 	if err := h.store.MarkFeedAsRead(userID, feedID, time.Now()); err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
-	json.NoContent(w, r)
+	response.JSONNoContent(w, r)
 }
 
 func (h *handler) getCategoryFeeds(w http.ResponseWriter, r *http.Request) {
@@ -160,58 +160,58 @@ func (h *handler) getCategoryFeeds(w http.ResponseWriter, r *http.Request) {
 
 	category, err := h.store.Category(userID, categoryID)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	if category == nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
 	feeds, err := h.store.FeedsByCategoryWithCounters(userID, categoryID)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
-	json.OK(w, r, feeds)
+	response.JSON(w, r, feeds)
 }
 
 func (h *handler) getFeeds(w http.ResponseWriter, r *http.Request) {
 	feeds, err := h.store.Feeds(request.UserID(r))
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
-	json.OK(w, r, feeds)
+	response.JSON(w, r, feeds)
 }
 
 func (h *handler) fetchCounters(w http.ResponseWriter, r *http.Request) {
 	counters, err := h.store.FetchCounters(request.UserID(r))
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
-	json.OK(w, r, counters)
+	response.JSON(w, r, counters)
 }
 
 func (h *handler) getFeed(w http.ResponseWriter, r *http.Request) {
 	feedID := request.RouteInt64Param(r, "feedID")
 	feed, err := h.store.FeedByID(request.UserID(r), feedID)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	if feed == nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
-	json.OK(w, r, feed)
+	response.JSON(w, r, feed)
 }
 
 func (h *handler) removeFeed(w http.ResponseWriter, r *http.Request) {
@@ -219,14 +219,14 @@ func (h *handler) removeFeed(w http.ResponseWriter, r *http.Request) {
 	userID := request.UserID(r)
 
 	if !h.store.FeedExists(userID, feedID) {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
 	if err := h.store.RemoveFeed(userID, feedID); err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
-	json.NoContent(w, r)
+	response.JSONNoContent(w, r)
 }

+ 7 - 7
internal/api/icon.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/json"
+	"miniflux.app/v2/internal/http/response"
 )
 
 func (h *handler) getIconByFeedID(w http.ResponseWriter, r *http.Request) {
@@ -15,16 +15,16 @@ func (h *handler) getIconByFeedID(w http.ResponseWriter, r *http.Request) {
 
 	icon, err := h.store.IconByFeedID(request.UserID(r), feedID)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	if icon == nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
-	json.OK(w, r, &feedIconResponse{
+	response.JSON(w, r, &feedIconResponse{
 		ID:       icon.ID,
 		MimeType: icon.MimeType,
 		Data:     icon.DataURL(),
@@ -36,16 +36,16 @@ func (h *handler) getIconByIconID(w http.ResponseWriter, r *http.Request) {
 
 	icon, err := h.store.IconByID(iconID)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	if icon == nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
-	json.OK(w, r, &feedIconResponse{
+	response.JSON(w, r, &feedIconResponse{
 		ID:       icon.ID,
 		MimeType: icon.MimeType,
 		Data:     icon.DataURL(),

+ 8 - 8
internal/api/middleware.go

@@ -9,7 +9,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/json"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/storage"
 )
 
@@ -51,7 +51,7 @@ func (m *middleware) apiKeyAuth(next http.Handler) http.Handler {
 
 		user, err := m.store.UserByAPIKey(token)
 		if err != nil {
-			json.ServerError(w, r, err)
+			response.JSONServerError(w, r, err)
 			return
 		}
 
@@ -62,7 +62,7 @@ func (m *middleware) apiKeyAuth(next http.Handler) http.Handler {
 				slog.String("user_agent", r.UserAgent()),
 				slog.String("request_uri", r.RequestURI),
 			)
-			json.Unauthorized(w, r)
+			response.JSONUnauthorized(w, r)
 			return
 		}
 
@@ -105,7 +105,7 @@ func (m *middleware) basicAuth(next http.Handler) http.Handler {
 				slog.String("user_agent", r.UserAgent()),
 				slog.String("request_uri", r.RequestURI),
 			)
-			json.Unauthorized(w, r)
+			response.JSONUnauthorized(w, r)
 			return
 		}
 
@@ -116,7 +116,7 @@ func (m *middleware) basicAuth(next http.Handler) http.Handler {
 				slog.String("user_agent", r.UserAgent()),
 				slog.String("request_uri", r.RequestURI),
 			)
-			json.Unauthorized(w, r)
+			response.JSONUnauthorized(w, r)
 			return
 		}
 
@@ -128,13 +128,13 @@ func (m *middleware) basicAuth(next http.Handler) http.Handler {
 				slog.String("username", username),
 				slog.String("request_uri", r.RequestURI),
 			)
-			json.Unauthorized(w, r)
+			response.JSONUnauthorized(w, r)
 			return
 		}
 
 		user, err := m.store.UserByUsername(username)
 		if err != nil {
-			json.ServerError(w, r, err)
+			response.JSONServerError(w, r, err)
 			return
 		}
 
@@ -146,7 +146,7 @@ func (m *middleware) basicAuth(next http.Handler) http.Handler {
 				slog.String("username", username),
 				slog.String("request_uri", r.RequestURI),
 			)
-			json.Unauthorized(w, r)
+			response.JSONUnauthorized(w, r)
 			return
 		}
 

+ 5 - 6
internal/api/opml.go

@@ -7,8 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/json"
-	"miniflux.app/v2/internal/http/response/xml"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/reader/opml"
 )
 
@@ -16,11 +15,11 @@ func (h *handler) exportFeeds(w http.ResponseWriter, r *http.Request) {
 	opmlHandler := opml.NewHandler(h.store)
 	opmlExport, err := opmlHandler.Export(request.UserID(r))
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
-	xml.OK(w, r, opmlExport)
+	response.XML(w, r, opmlExport)
 }
 
 func (h *handler) importFeeds(w http.ResponseWriter, r *http.Request) {
@@ -28,9 +27,9 @@ func (h *handler) importFeeds(w http.ResponseWriter, r *http.Request) {
 	err := opmlHandler.Import(request.UserID(r), r.Body)
 	defer r.Body.Close()
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
-	json.Created(w, r, importFeedsResponse{Message: "Feeds imported successfully"})
+	response.JSONCreated(w, r, importFeedsResponse{Message: "Feeds imported successfully"})
 }

+ 6 - 6
internal/api/subscription.go

@@ -9,7 +9,7 @@ import (
 
 	"miniflux.app/v2/internal/config"
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/json"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/proxyrotator"
 	"miniflux.app/v2/internal/reader/fetcher"
@@ -20,12 +20,12 @@ import (
 func (h *handler) discoverSubscriptions(w http.ResponseWriter, r *http.Request) {
 	var subscriptionDiscoveryRequest model.SubscriptionDiscoveryRequest
 	if err := json_parser.NewDecoder(r.Body).Decode(&subscriptionDiscoveryRequest); err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
 	if validationErr := validator.ValidateSubscriptionDiscovery(&subscriptionDiscoveryRequest); validationErr != nil {
-		json.BadRequest(w, r, validationErr.Error())
+		response.JSONBadRequest(w, r, validationErr.Error())
 		return
 	}
 
@@ -56,14 +56,14 @@ func (h *handler) discoverSubscriptions(w http.ResponseWriter, r *http.Request)
 	)
 
 	if localizedError != nil {
-		json.ServerError(w, r, localizedError.Error())
+		response.JSONServerError(w, r, localizedError.Error())
 		return
 	}
 
 	if len(subscriptions) == 0 {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
-	json.OK(w, r, subscriptions)
+	response.JSON(w, r, subscriptions)
 }

+ 38 - 38
internal/api/user.go

@@ -9,7 +9,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/json"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/validator"
 )
@@ -17,37 +17,37 @@ import (
 func (h *handler) currentUser(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
-	json.OK(w, r, user)
+	response.JSON(w, r, user)
 }
 
 func (h *handler) createUser(w http.ResponseWriter, r *http.Request) {
 	if !request.IsAdminUser(r) {
-		json.Forbidden(w, r)
+		response.JSONForbidden(w, r)
 		return
 	}
 
 	var userCreationRequest model.UserCreationRequest
 	if err := json_parser.NewDecoder(r.Body).Decode(&userCreationRequest); err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
 	if validationErr := validator.ValidateUserCreationWithPassword(h.store, &userCreationRequest); validationErr != nil {
-		json.BadRequest(w, r, validationErr.Error())
+		response.JSONBadRequest(w, r, validationErr.Error())
 		return
 	}
 
 	user, err := h.store.CreateUser(&userCreationRequest)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
-	json.Created(w, r, user)
+	response.JSONCreated(w, r, user)
 }
 
 func (h *handler) updateUser(w http.ResponseWriter, r *http.Request) {
@@ -55,162 +55,162 @@ func (h *handler) updateUser(w http.ResponseWriter, r *http.Request) {
 
 	var userModificationRequest model.UserModificationRequest
 	if err := json_parser.NewDecoder(r.Body).Decode(&userModificationRequest); err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
 	originalUser, err := h.store.UserByID(userID)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	if originalUser == nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
 	if !request.IsAdminUser(r) {
 		if originalUser.ID != request.UserID(r) {
-			json.Forbidden(w, r)
+			response.JSONForbidden(w, r)
 			return
 		}
 
 		if userModificationRequest.IsAdmin != nil && *userModificationRequest.IsAdmin {
-			json.BadRequest(w, r, errors.New("only administrators can change permissions of standard users"))
+			response.JSONBadRequest(w, r, errors.New("only administrators can change permissions of standard users"))
 			return
 		}
 	}
 
 	if validationErr := validator.ValidateUserModification(h.store, originalUser.ID, &userModificationRequest); validationErr != nil {
-		json.BadRequest(w, r, validationErr.Error())
+		response.JSONBadRequest(w, r, validationErr.Error())
 		return
 	}
 
 	userModificationRequest.Patch(originalUser)
 	if err = h.store.UpdateUser(originalUser); err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
-	json.Created(w, r, originalUser)
+	response.JSONCreated(w, r, originalUser)
 }
 
 func (h *handler) markUserAsRead(w http.ResponseWriter, r *http.Request) {
 	userID := request.RouteInt64Param(r, "userID")
 	if userID != request.UserID(r) {
-		json.Forbidden(w, r)
+		response.JSONForbidden(w, r)
 		return
 	}
 
 	if _, err := h.store.UserByID(userID); err != nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
 	if err := h.store.MarkAllAsRead(userID); err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
-	json.NoContent(w, r)
+	response.JSONNoContent(w, r)
 }
 
 func (h *handler) getIntegrationsStatus(w http.ResponseWriter, r *http.Request) {
 	userID := request.UserID(r)
 
 	if _, err := h.store.UserByID(userID); err != nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
 	hasIntegrations := h.store.HasSaveEntry(userID)
 
-	json.OK(w, r, integrationsStatusResponse{HasIntegrations: hasIntegrations})
+	response.JSON(w, r, integrationsStatusResponse{HasIntegrations: hasIntegrations})
 }
 
 func (h *handler) users(w http.ResponseWriter, r *http.Request) {
 	if !request.IsAdminUser(r) {
-		json.Forbidden(w, r)
+		response.JSONForbidden(w, r)
 		return
 	}
 
 	users, err := h.store.Users()
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	users.UseTimezone(request.UserTimezone(r))
-	json.OK(w, r, users)
+	response.JSON(w, r, users)
 }
 
 func (h *handler) userByID(w http.ResponseWriter, r *http.Request) {
 	if !request.IsAdminUser(r) {
-		json.Forbidden(w, r)
+		response.JSONForbidden(w, r)
 		return
 	}
 
 	userID := request.RouteInt64Param(r, "userID")
 	user, err := h.store.UserByID(userID)
 	if err != nil {
-		json.BadRequest(w, r, errors.New("unable to fetch this user from the database"))
+		response.JSONBadRequest(w, r, errors.New("unable to fetch this user from the database"))
 		return
 	}
 
 	if user == nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
 	user.UseTimezone(request.UserTimezone(r))
-	json.OK(w, r, user)
+	response.JSON(w, r, user)
 }
 
 func (h *handler) userByUsername(w http.ResponseWriter, r *http.Request) {
 	if !request.IsAdminUser(r) {
-		json.Forbidden(w, r)
+		response.JSONForbidden(w, r)
 		return
 	}
 
 	username := request.RouteStringParam(r, "username")
 	user, err := h.store.UserByUsername(username)
 	if err != nil {
-		json.BadRequest(w, r, errors.New("unable to fetch this user from the database"))
+		response.JSONBadRequest(w, r, errors.New("unable to fetch this user from the database"))
 		return
 	}
 
 	if user == nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
-	json.OK(w, r, user)
+	response.JSON(w, r, user)
 }
 
 func (h *handler) removeUser(w http.ResponseWriter, r *http.Request) {
 	if !request.IsAdminUser(r) {
-		json.Forbidden(w, r)
+		response.JSONForbidden(w, r)
 		return
 	}
 
 	userID := request.RouteInt64Param(r, "userID")
 	user, err := h.store.UserByID(userID)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	if user == nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
 	if user.ID == request.UserID(r) {
-		json.BadRequest(w, r, errors.New("you cannot remove yourself"))
+		response.JSONBadRequest(w, r, errors.New("you cannot remove yourself"))
 		return
 	}
 
 	h.store.RemoveUserAsync(user.ID)
-	json.NoContent(w, r)
+	response.JSONNoContent(w, r)
 }

+ 24 - 24
internal/fever/handler.go

@@ -11,7 +11,7 @@ import (
 	"time"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/json"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/integration"
 	"miniflux.app/v2/internal/mediaproxy"
 	"miniflux.app/v2/internal/model"
@@ -55,7 +55,7 @@ func (h *handler) serve(w http.ResponseWriter, r *http.Request) {
 	case r.FormValue("mark") == "group":
 		h.handleWriteGroups(w, r)
 	default:
-		json.OK(w, r, newBaseResponse())
+		response.JSON(w, r, newBaseResponse())
 	}
 }
 
@@ -86,13 +86,13 @@ func (h *handler) handleGroups(w http.ResponseWriter, r *http.Request) {
 
 	categories, err := h.store.Categories(userID)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	feeds, err := h.store.Feeds(userID)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
@@ -103,7 +103,7 @@ func (h *handler) handleGroups(w http.ResponseWriter, r *http.Request) {
 
 	result.FeedsGroups = h.buildFeedGroups(feeds)
 	result.SetCommonValues()
-	json.OK(w, r, result)
+	response.JSON(w, r, result)
 }
 
 /*
@@ -138,7 +138,7 @@ func (h *handler) handleFeeds(w http.ResponseWriter, r *http.Request) {
 
 	feeds, err := h.store.Feeds(userID)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
@@ -163,7 +163,7 @@ func (h *handler) handleFeeds(w http.ResponseWriter, r *http.Request) {
 
 	result.FeedsGroups = h.buildFeedGroups(feeds)
 	result.SetCommonValues()
-	json.OK(w, r, result)
+	response.JSON(w, r, result)
 }
 
 /*
@@ -193,7 +193,7 @@ func (h *handler) handleFavicons(w http.ResponseWriter, r *http.Request) {
 
 	icons, err := h.store.Icons(userID)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
@@ -206,7 +206,7 @@ func (h *handler) handleFavicons(w http.ResponseWriter, r *http.Request) {
 	}
 
 	result.SetCommonValues()
-	json.OK(w, r, result)
+	response.JSON(w, r, result)
 }
 
 /*
@@ -295,7 +295,7 @@ func (h *handler) handleItems(w http.ResponseWriter, r *http.Request) {
 
 	entries, err := builder.GetEntries()
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
@@ -303,7 +303,7 @@ func (h *handler) handleItems(w http.ResponseWriter, r *http.Request) {
 	builder.WithoutStatus(model.EntryStatusRemoved)
 	result.Total, err = builder.CountEntries()
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
@@ -333,7 +333,7 @@ func (h *handler) handleItems(w http.ResponseWriter, r *http.Request) {
 	}
 
 	result.SetCommonValues()
-	json.OK(w, r, result)
+	response.JSON(w, r, result)
 }
 
 /*
@@ -354,7 +354,7 @@ func (h *handler) handleUnreadItems(w http.ResponseWriter, r *http.Request) {
 	builder.WithStatus(model.EntryStatusUnread)
 	rawEntryIDs, err := builder.GetEntryIDs()
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
@@ -366,7 +366,7 @@ func (h *handler) handleUnreadItems(w http.ResponseWriter, r *http.Request) {
 	var result unreadResponse
 	result.ItemIDs = strings.Join(itemIDs, ",")
 	result.SetCommonValues()
-	json.OK(w, r, result)
+	response.JSON(w, r, result)
 }
 
 /*
@@ -388,7 +388,7 @@ func (h *handler) handleSavedItems(w http.ResponseWriter, r *http.Request) {
 
 	entryIDs, err := builder.GetEntryIDs()
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
@@ -399,7 +399,7 @@ func (h *handler) handleSavedItems(w http.ResponseWriter, r *http.Request) {
 
 	result := &savedResponse{ItemIDs: strings.Join(itemsIDs, ",")}
 	result.SetCommonValues()
-	json.OK(w, r, result)
+	response.JSON(w, r, result)
 }
 
 /*
@@ -424,7 +424,7 @@ func (h *handler) handleWriteItems(w http.ResponseWriter, r *http.Request) {
 
 	entry, err := builder.GetEntry()
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
@@ -433,7 +433,7 @@ func (h *handler) handleWriteItems(w http.ResponseWriter, r *http.Request) {
 			slog.Int64("user_id", userID),
 			slog.Int64("entry_id", entryID),
 		)
-		json.OK(w, r, newBaseResponse())
+		response.JSON(w, r, newBaseResponse())
 		return
 	}
 
@@ -456,13 +456,13 @@ func (h *handler) handleWriteItems(w http.ResponseWriter, r *http.Request) {
 			slog.Int64("entry_id", entryID),
 		)
 		if err := h.store.ToggleStarred(userID, entryID); err != nil {
-			json.ServerError(w, r, err)
+			response.JSONServerError(w, r, err)
 			return
 		}
 
 		settings, err := h.store.Integration(userID)
 		if err != nil {
-			json.ServerError(w, r, err)
+			response.JSONServerError(w, r, err)
 			return
 		}
 
@@ -475,12 +475,12 @@ func (h *handler) handleWriteItems(w http.ResponseWriter, r *http.Request) {
 			slog.Int64("entry_id", entryID),
 		)
 		if err := h.store.ToggleStarred(userID, entryID); err != nil {
-			json.ServerError(w, r, err)
+			response.JSONServerError(w, r, err)
 			return
 		}
 	}
 
-	json.OK(w, r, newBaseResponse())
+	response.JSON(w, r, newBaseResponse())
 }
 
 /*
@@ -515,7 +515,7 @@ func (h *handler) handleWriteFeeds(w http.ResponseWriter, r *http.Request) {
 		}
 	}()
 
-	json.OK(w, r, newBaseResponse())
+	response.JSON(w, r, newBaseResponse())
 }
 
 /*
@@ -558,7 +558,7 @@ func (h *handler) handleWriteGroups(w http.ResponseWriter, r *http.Request) {
 		}
 	}()
 
-	json.OK(w, r, newBaseResponse())
+	response.JSON(w, r, newBaseResponse())
 }
 
 /*

+ 4 - 4
internal/fever/middleware.go

@@ -9,7 +9,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/json"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/storage"
 )
 
@@ -31,7 +31,7 @@ func (m *middleware) serve(next http.Handler) http.Handler {
 				slog.String("client_ip", clientIP),
 				slog.String("user_agent", r.UserAgent()),
 			)
-			json.OK(w, r, newAuthFailureResponse())
+			response.JSON(w, r, newAuthFailureResponse())
 			return
 		}
 
@@ -43,7 +43,7 @@ func (m *middleware) serve(next http.Handler) http.Handler {
 				slog.String("user_agent", r.UserAgent()),
 				slog.Any("error", err),
 			)
-			json.OK(w, r, newAuthFailureResponse())
+			response.JSON(w, r, newAuthFailureResponse())
 			return
 		}
 
@@ -53,7 +53,7 @@ func (m *middleware) serve(next http.Handler) http.Handler {
 				slog.String("client_ip", clientIP),
 				slog.String("user_agent", r.UserAgent()),
 			)
-			json.OK(w, r, newAuthFailureResponse())
+			response.JSON(w, r, newAuthFailureResponse())
 			return
 		}
 

+ 87 - 88
internal/googlereader/handler.go

@@ -14,7 +14,6 @@ import (
 	"miniflux.app/v2/internal/config"
 	"miniflux.app/v2/internal/http/request"
 	"miniflux.app/v2/internal/http/response"
-	"miniflux.app/v2/internal/http/response/json"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/integration"
 	"miniflux.app/v2/internal/mediaproxy"
@@ -147,7 +146,7 @@ func (h *handler) clientLoginHandler(w http.ResponseWriter, r *http.Request) {
 			slog.String("user_agent", r.UserAgent()),
 			slog.Any("error", err),
 		)
-		json.Unauthorized(w, r)
+		response.JSONUnauthorized(w, r)
 		return
 	}
 
@@ -161,7 +160,7 @@ func (h *handler) clientLoginHandler(w http.ResponseWriter, r *http.Request) {
 			slog.String("client_ip", clientIP),
 			slog.String("user_agent", r.UserAgent()),
 		)
-		json.Unauthorized(w, r)
+		response.JSONUnauthorized(w, r)
 		return
 	}
 
@@ -173,7 +172,7 @@ func (h *handler) clientLoginHandler(w http.ResponseWriter, r *http.Request) {
 			slog.String("username", username),
 			slog.Any("error", err),
 		)
-		json.Unauthorized(w, r)
+		response.JSONUnauthorized(w, r)
 		return
 	}
 
@@ -186,7 +185,7 @@ func (h *handler) clientLoginHandler(w http.ResponseWriter, r *http.Request) {
 
 	integration, err := h.store.GoogleReaderUserGetIntegration(username)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
@@ -201,7 +200,7 @@ func (h *handler) clientLoginHandler(w http.ResponseWriter, r *http.Request) {
 
 	result := loginResponse{SID: token, LSID: token, Auth: token}
 	if output == "json" {
-		json.OK(w, r, result)
+		response.JSON(w, r, result)
 		return
 	}
 
@@ -225,7 +224,7 @@ func (h *handler) tokenHandler(w http.ResponseWriter, r *http.Request) {
 			slog.String("client_ip", clientIP),
 			slog.String("user_agent", r.UserAgent()),
 		)
-		json.Unauthorized(w, r)
+		response.JSONUnauthorized(w, r)
 		return
 	}
 
@@ -236,7 +235,7 @@ func (h *handler) tokenHandler(w http.ResponseWriter, r *http.Request) {
 			slog.String("user_agent", r.UserAgent()),
 			slog.Int64("user_id", request.UserID(r)),
 		)
-		json.Unauthorized(w, r)
+		response.JSONUnauthorized(w, r)
 		return
 	}
 
@@ -263,34 +262,34 @@ func (h *handler) editTagHandler(w http.ResponseWriter, r *http.Request) {
 	)
 
 	if err := r.ParseForm(); err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	addTags, err := getStreams(r.PostForm[paramTagsAdd], userID)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 	removeTags, err := getStreams(r.PostForm[paramTagsRemove], userID)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 	if len(addTags) == 0 && len(removeTags) == 0 {
 		err = errors.New("googlreader: add or/and remove tags should be supplied")
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 	tags, err := checkAndSimplifyTags(addTags, removeTags)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	itemIDs, err := parseItemIDsFromRequest(r)
 	if err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
@@ -309,7 +308,7 @@ func (h *handler) editTagHandler(w http.ResponseWriter, r *http.Request) {
 
 	entries, err := builder.GetEntries()
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
@@ -341,7 +340,7 @@ func (h *handler) editTagHandler(w http.ResponseWriter, r *http.Request) {
 	if len(readEntryIDs) > 0 {
 		err = h.store.SetEntriesStatus(userID, readEntryIDs, model.EntryStatusRead)
 		if err != nil {
-			json.ServerError(w, r, err)
+			response.JSONServerError(w, r, err)
 			return
 		}
 	}
@@ -349,7 +348,7 @@ func (h *handler) editTagHandler(w http.ResponseWriter, r *http.Request) {
 	if len(unreadEntryIDs) > 0 {
 		err = h.store.SetEntriesStatus(userID, unreadEntryIDs, model.EntryStatusUnread)
 		if err != nil {
-			json.ServerError(w, r, err)
+			response.JSONServerError(w, r, err)
 			return
 		}
 	}
@@ -357,7 +356,7 @@ func (h *handler) editTagHandler(w http.ResponseWriter, r *http.Request) {
 	if len(unstarredEntryIDs) > 0 {
 		err = h.store.SetEntriesStarredState(userID, unstarredEntryIDs, false)
 		if err != nil {
-			json.ServerError(w, r, err)
+			response.JSONServerError(w, r, err)
 			return
 		}
 	}
@@ -365,7 +364,7 @@ func (h *handler) editTagHandler(w http.ResponseWriter, r *http.Request) {
 	if len(starredEntryIDs) > 0 {
 		err = h.store.SetEntriesStarredState(userID, starredEntryIDs, true)
 		if err != nil {
-			json.ServerError(w, r, err)
+			response.JSONServerError(w, r, err)
 			return
 		}
 	}
@@ -373,7 +372,7 @@ func (h *handler) editTagHandler(w http.ResponseWriter, r *http.Request) {
 	if len(entries) > 0 {
 		settings, err := h.store.Integration(userID)
 		if err != nil {
-			json.ServerError(w, r, err)
+			response.JSONServerError(w, r, err)
 			return
 		}
 
@@ -401,13 +400,13 @@ func (h *handler) quickAddHandler(w http.ResponseWriter, r *http.Request) {
 
 	err := r.ParseForm()
 	if err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
 	feedURL := r.Form.Get(paramQuickAdd)
 	if !urllib.IsAbsoluteURL(feedURL) {
-		json.BadRequest(w, r, fmt.Errorf("googlereader: invalid URL: %s", feedURL))
+		response.JSONBadRequest(w, r, fmt.Errorf("googlereader: invalid URL: %s", feedURL))
 		return
 	}
 
@@ -424,12 +423,12 @@ func (h *handler) quickAddHandler(w http.ResponseWriter, r *http.Request) {
 
 	subscriptions, localizedError := mfs.NewSubscriptionFinder(requestBuilder).FindSubscriptions(feedURL, rssBridgeURL, rssBridgeToken)
 	if localizedError != nil {
-		json.ServerError(w, r, localizedError.Error())
+		response.JSONServerError(w, r, localizedError.Error())
 		return
 	}
 
 	if len(subscriptions) == 0 {
-		json.OK(w, r, quickAddResponse{
+		response.JSON(w, r, quickAddResponse{
 			NumResults: 0,
 		})
 		return
@@ -439,7 +438,7 @@ func (h *handler) quickAddHandler(w http.ResponseWriter, r *http.Request) {
 	category := Stream{NoStream, ""}
 	newFeed, err := subscribe(toSubscribe, category, "", h.store, userID)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
@@ -451,7 +450,7 @@ func (h *handler) quickAddHandler(w http.ResponseWriter, r *http.Request) {
 		slog.String("feed_url", newFeed.FeedURL),
 	)
 
-	json.OK(w, r, quickAddResponse{
+	response.JSON(w, r, quickAddResponse{
 		NumResults: 1,
 		Query:      newFeed.FeedURL,
 		StreamID:   feedPrefix + strconv.FormatInt(newFeed.ID, 10),
@@ -602,19 +601,19 @@ func (h *handler) editSubscriptionHandler(w http.ResponseWriter, r *http.Request
 	)
 
 	if err := r.ParseForm(); err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
 	streamIds, err := getStreams(r.Form[paramStreamID], userID)
 	if err != nil || len(streamIds) == 0 {
-		json.BadRequest(w, r, errors.New("googlereader: no valid stream IDs provided"))
+		response.JSONBadRequest(w, r, errors.New("googlereader: no valid stream IDs provided"))
 		return
 	}
 
 	newLabel, err := getStream(r.Form.Get(paramTagsAdd), userID)
 	if err != nil {
-		json.BadRequest(w, r, fmt.Errorf("googlereader: invalid data in %s", paramTagsAdd))
+		response.JSONBadRequest(w, r, fmt.Errorf("googlereader: invalid data in %s", paramTagsAdd))
 		return
 	}
 
@@ -625,22 +624,22 @@ func (h *handler) editSubscriptionHandler(w http.ResponseWriter, r *http.Request
 	case "subscribe":
 		_, err := subscribe(streamIds[0], newLabel, title, h.store, userID)
 		if err != nil {
-			json.ServerError(w, r, err)
+			response.JSONServerError(w, r, err)
 			return
 		}
 	case "unsubscribe":
 		err := unsubscribe(streamIds, h.store, userID)
 		if err != nil {
-			json.ServerError(w, r, err)
+			response.JSONServerError(w, r, err)
 			return
 		}
 	case "edit":
 		if title != "" {
 			if err := rename(streamIds[0], title, h.store, userID); err != nil {
 				if errors.Is(err, errFeedNotFound) || errors.Is(err, errEmptyFeedTitle) {
-					json.BadRequest(w, r, err)
+					response.JSONBadRequest(w, r, err)
 				} else {
-					json.ServerError(w, r, err)
+					response.JSONServerError(w, r, err)
 				}
 				return
 			}
@@ -648,21 +647,21 @@ func (h *handler) editSubscriptionHandler(w http.ResponseWriter, r *http.Request
 
 		if r.Form.Has(paramTagsAdd) {
 			if newLabel.Type != LabelStream {
-				json.BadRequest(w, r, errors.New("destination must be a label"))
+				response.JSONBadRequest(w, r, errors.New("destination must be a label"))
 				return
 			}
 
 			if err := move(streamIds[0], newLabel, h.store, userID); err != nil {
 				if errors.Is(err, errFeedNotFound) || errors.Is(err, errCategoryNotFound) {
-					json.BadRequest(w, r, err)
+					response.JSONBadRequest(w, r, err)
 				} else {
-					json.ServerError(w, r, err)
+					response.JSONServerError(w, r, err)
 				}
 				return
 			}
 		}
 	default:
-		json.BadRequest(w, r, fmt.Errorf("googlereader: unrecognized action %s", action))
+		response.JSONBadRequest(w, r, fmt.Errorf("googlereader: unrecognized action %s", action))
 		return
 	}
 
@@ -682,19 +681,19 @@ func (h *handler) streamItemContentsHandler(w http.ResponseWriter, r *http.Reque
 	)
 
 	if err := checkOutputFormat(r); err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
 	err := r.ParseForm()
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	requestModifiers, err := parseStreamFilterFromRequest(r)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
@@ -705,7 +704,7 @@ func (h *handler) streamItemContentsHandler(w http.ResponseWriter, r *http.Reque
 
 	itemIDs, err := parseItemIDsFromRequest(r)
 	if err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
@@ -725,7 +724,7 @@ func (h *handler) streamItemContentsHandler(w http.ResponseWriter, r *http.Reque
 
 	entries, err := builder.GetEntries()
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
@@ -800,7 +799,7 @@ func (h *handler) streamItemContentsHandler(w http.ResponseWriter, r *http.Reque
 		}
 	}
 
-	json.OK(w, r, result)
+	response.JSON(w, r, result)
 }
 
 func (h *handler) disableTagHandler(w http.ResponseWriter, r *http.Request) {
@@ -816,20 +815,20 @@ func (h *handler) disableTagHandler(w http.ResponseWriter, r *http.Request) {
 
 	err := r.ParseForm()
 	if err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
 	streams, err := getStreams(r.Form[paramStreamID], userID)
 	if err != nil {
-		json.BadRequest(w, r, fmt.Errorf("googlereader: invalid data in %s", paramStreamID))
+		response.JSONBadRequest(w, r, fmt.Errorf("googlereader: invalid data in %s", paramStreamID))
 		return
 	}
 
 	titles := make([]string, len(streams))
 	for i, stream := range streams {
 		if stream.Type != LabelStream {
-			json.BadRequest(w, r, errors.New("googlereader: only labels are supported"))
+			response.JSONBadRequest(w, r, errors.New("googlereader: only labels are supported"))
 			return
 		}
 		titles[i] = stream.ID
@@ -837,7 +836,7 @@ func (h *handler) disableTagHandler(w http.ResponseWriter, r *http.Request) {
 
 	err = h.store.RemoveAndReplaceCategoriesByName(userID, titles)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
@@ -856,39 +855,39 @@ func (h *handler) renameTagHandler(w http.ResponseWriter, r *http.Request) {
 
 	err := r.ParseForm()
 	if err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
 	source, err := getStream(r.Form.Get(paramStreamID), userID)
 	if err != nil {
-		json.BadRequest(w, r, fmt.Errorf("googlereader: invalid data in %s", paramStreamID))
+		response.JSONBadRequest(w, r, fmt.Errorf("googlereader: invalid data in %s", paramStreamID))
 		return
 	}
 
 	destination, err := getStream(r.Form.Get(paramDestination), userID)
 	if err != nil {
-		json.BadRequest(w, r, fmt.Errorf("googlereader: invalid data in %s", paramDestination))
+		response.JSONBadRequest(w, r, fmt.Errorf("googlereader: invalid data in %s", paramDestination))
 		return
 	}
 
 	if source.Type != LabelStream || destination.Type != LabelStream {
-		json.BadRequest(w, r, errors.New("googlereader: only labels supported"))
+		response.JSONBadRequest(w, r, errors.New("googlereader: only labels supported"))
 		return
 	}
 
 	if destination.ID == "" {
-		json.BadRequest(w, r, errors.New("googlereader: empty destination name"))
+		response.JSONBadRequest(w, r, errors.New("googlereader: empty destination name"))
 		return
 	}
 
 	category, err := h.store.CategoryByTitle(userID, source.ID)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 	if category == nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
@@ -897,14 +896,14 @@ func (h *handler) renameTagHandler(w http.ResponseWriter, r *http.Request) {
 	}
 
 	if validationError := validator.ValidateCategoryModification(h.store, userID, category.ID, &categoryModificationRequest); validationError != nil {
-		json.BadRequest(w, r, validationError.Error())
+		response.JSONBadRequest(w, r, validationError.Error())
 		return
 	}
 
 	categoryModificationRequest.Patch(category)
 
 	if err := h.store.UpdateCategory(category); err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
@@ -922,14 +921,14 @@ func (h *handler) tagListHandler(w http.ResponseWriter, r *http.Request) {
 	)
 
 	if err := checkOutputFormat(r); err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
 	var result tagsResponse
 	categories, err := h.store.Categories(userID)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 	result.Tags = make([]subscriptionCategoryResponse, 0)
@@ -944,7 +943,7 @@ func (h *handler) tagListHandler(w http.ResponseWriter, r *http.Request) {
 			Type:  "folder",
 		})
 	}
-	json.OK(w, r, result)
+	response.JSON(w, r, result)
 }
 
 func (h *handler) subscriptionListHandler(w http.ResponseWriter, r *http.Request) {
@@ -958,14 +957,14 @@ func (h *handler) subscriptionListHandler(w http.ResponseWriter, r *http.Request
 	)
 
 	if err := checkOutputFormat(r); err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
 	var result subscriptionsResponse
 	feeds, err := h.store.Feeds(userID)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
@@ -981,7 +980,7 @@ func (h *handler) subscriptionListHandler(w http.ResponseWriter, r *http.Request
 			IconURL:    h.feedIconURL(feed),
 		})
 	}
-	json.OK(w, r, result)
+	response.JSON(w, r, result)
 }
 
 func (h *handler) serveHandler(w http.ResponseWriter, r *http.Request) {
@@ -993,7 +992,7 @@ func (h *handler) serveHandler(w http.ResponseWriter, r *http.Request) {
 		slog.String("user_agent", r.UserAgent()),
 	)
 
-	json.OK(w, r, []string{})
+	response.JSON(w, r, []string{})
 }
 
 func (h *handler) userInfoHandler(w http.ResponseWriter, r *http.Request) {
@@ -1007,16 +1006,16 @@ func (h *handler) userInfoHandler(w http.ResponseWriter, r *http.Request) {
 
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 	if user == nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
 	userInfo := userInfoResponse{UserID: strconv.FormatInt(user.ID, 10), UserName: user.Username, UserProfileID: strconv.FormatInt(user.ID, 10), UserEmail: user.Username}
-	json.OK(w, r, userInfo)
+	response.JSON(w, r, userInfo)
 }
 
 func (h *handler) streamItemIDsHandler(w http.ResponseWriter, r *http.Request) {
@@ -1031,13 +1030,13 @@ func (h *handler) streamItemIDsHandler(w http.ResponseWriter, r *http.Request) {
 	)
 
 	if err := checkOutputFormat(r); err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
 	rm, err := parseStreamFilterFromRequest(r)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
@@ -1049,7 +1048,7 @@ func (h *handler) streamItemIDsHandler(w http.ResponseWriter, r *http.Request) {
 	)
 
 	if len(rm.Streams) != 1 {
-		json.ServerError(w, r, errors.New("googlereader: only one stream type expected"))
+		response.JSONServerError(w, r, errors.New("googlereader: only one stream type expected"))
 		return
 	}
 	switch rm.Streams[0].Type {
@@ -1068,7 +1067,7 @@ func (h *handler) streamItemIDsHandler(w http.ResponseWriter, r *http.Request) {
 			slog.String("user_agent", r.UserAgent()),
 			slog.Any("stream_type", rm.Streams[0].Type),
 		)
-		json.ServerError(w, r, fmt.Errorf("googlereader: unknown stream type %s", rm.Streams[0].Type))
+		response.JSONServerError(w, r, fmt.Errorf("googlereader: unknown stream type %s", rm.Streams[0].Type))
 	}
 }
 
@@ -1109,10 +1108,10 @@ func (h *handler) handleReadingListStreamHandler(w http.ResponseWriter, r *http.
 
 	itemRefs, continuation, err := getItemRefsAndContinuation(*builder, rm)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
-	json.OK(w, r, streamIDResponse{itemRefs, continuation})
+	response.JSON(w, r, streamIDResponse{itemRefs, continuation})
 }
 
 func (h *handler) handleStarredStreamHandler(w http.ResponseWriter, r *http.Request, rm requestModifiers) {
@@ -1130,10 +1129,10 @@ func (h *handler) handleStarredStreamHandler(w http.ResponseWriter, r *http.Requ
 	}
 	itemRefs, continuation, err := getItemRefsAndContinuation(*builder, rm)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
-	json.OK(w, r, streamIDResponse{itemRefs, continuation})
+	response.JSON(w, r, streamIDResponse{itemRefs, continuation})
 }
 
 func (h *handler) handleReadStreamHandler(w http.ResponseWriter, r *http.Request, rm requestModifiers) {
@@ -1152,10 +1151,10 @@ func (h *handler) handleReadStreamHandler(w http.ResponseWriter, r *http.Request
 
 	itemRefs, continuation, err := getItemRefsAndContinuation(*builder, rm)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
-	json.OK(w, r, streamIDResponse{itemRefs, continuation})
+	response.JSON(w, r, streamIDResponse{itemRefs, continuation})
 }
 
 func getItemRefsAndContinuation(builder storage.EntryQueryBuilder, rm requestModifiers) ([]itemRef, int, error) {
@@ -1183,7 +1182,7 @@ func getItemRefsAndContinuation(builder storage.EntryQueryBuilder, rm requestMod
 func (h *handler) handleFeedStreamHandler(w http.ResponseWriter, r *http.Request, rm requestModifiers) {
 	feedID, err := strconv.ParseInt(rm.Streams[0].ID, 10, 64)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
@@ -1211,10 +1210,10 @@ func (h *handler) handleFeedStreamHandler(w http.ResponseWriter, r *http.Request
 	}
 	itemRefs, continuation, err := getItemRefsAndContinuation(*builder, rm)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
-	json.OK(w, r, streamIDResponse{itemRefs, continuation})
+	response.JSON(w, r, streamIDResponse{itemRefs, continuation})
 }
 
 func (h *handler) markAllAsReadHandler(w http.ResponseWriter, r *http.Request) {
@@ -1228,13 +1227,13 @@ func (h *handler) markAllAsReadHandler(w http.ResponseWriter, r *http.Request) {
 	)
 
 	if err := r.ParseForm(); err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
 	stream, err := getStream(r.Form.Get(paramStreamID), userID)
 	if err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
@@ -1242,7 +1241,7 @@ func (h *handler) markAllAsReadHandler(w http.ResponseWriter, r *http.Request) {
 	if timestampParamValue := r.Form.Get(paramTimestamp); timestampParamValue != "" {
 		timestampParsedValue, err := strconv.ParseInt(timestampParamValue, 10, 64)
 		if err != nil {
-			json.BadRequest(w, r, err)
+			response.JSONBadRequest(w, r, err)
 			return
 		}
 
@@ -1264,31 +1263,31 @@ func (h *handler) markAllAsReadHandler(w http.ResponseWriter, r *http.Request) {
 	case FeedStream:
 		feedID, err := strconv.ParseInt(stream.ID, 10, 64)
 		if err != nil {
-			json.BadRequest(w, r, err)
+			response.JSONBadRequest(w, r, err)
 			return
 		}
 		err = h.store.MarkFeedAsRead(userID, feedID, before)
 		if err != nil {
-			json.ServerError(w, r, err)
+			response.JSONServerError(w, r, err)
 			return
 		}
 	case LabelStream:
 		category, err := h.store.CategoryByTitle(userID, stream.ID)
 		if err != nil {
-			json.ServerError(w, r, err)
+			response.JSONServerError(w, r, err)
 			return
 		}
 		if category == nil {
-			json.NotFound(w, r)
+			response.JSONNotFound(w, r)
 			return
 		}
 		if err := h.store.MarkCategoryAsRead(userID, category.ID, before); err != nil {
-			json.ServerError(w, r, err)
+			response.JSONServerError(w, r, err)
 			return
 		}
 	case ReadingListStream:
 		if err = h.store.MarkAllAsReadBeforeDate(userID, before); err != nil {
-			json.ServerError(w, r, err)
+			response.JSONServerError(w, r, err)
 			return
 		}
 	}

+ 23 - 24
internal/http/response/html/html.go → internal/http/response/html.go

@@ -1,7 +1,7 @@
 // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
 // SPDX-License-Identifier: Apache-2.0
 
-package html // import "miniflux.app/v2/internal/http/response/html"
+package response // import "miniflux.app/v2/internal/http/response"
 
 import (
 	"html"
@@ -9,20 +9,19 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response"
 )
 
-// OK creates a new HTML response with a 200 status code.
-func OK[T []byte | string](w http.ResponseWriter, r *http.Request, body T) {
-	builder := response.New(w, r)
+// HTML creates a new HTML response with a 200 status code.
+func HTML[T []byte | string](w http.ResponseWriter, r *http.Request, body T) {
+	builder := New(w, r)
 	builder.WithHeader("Content-Type", "text/html; charset=utf-8")
 	builder.WithHeader("Cache-Control", "no-cache, max-age=0, must-revalidate, no-store")
 	builder.WithBody(body)
 	builder.Write()
 }
 
-// ServerError sends an internal error to the client.
-func ServerError(w http.ResponseWriter, r *http.Request, err error) {
+// HTMLServerError sends an internal error to the client.
+func HTMLServerError(w http.ResponseWriter, r *http.Request, err error) {
 	slog.Error(http.StatusText(http.StatusInternalServerError),
 		slog.Any("error", err),
 		slog.String("client_ip", request.ClientIP(r)),
@@ -36,17 +35,17 @@ func ServerError(w http.ResponseWriter, r *http.Request, err error) {
 		),
 	)
 
-	builder := response.New(w, r)
+	builder := New(w, r)
 	builder.WithStatus(http.StatusInternalServerError)
-	builder.WithHeader("Content-Security-Policy", response.ContentSecurityPolicyForUntrustedContent)
+	builder.WithHeader("Content-Security-Policy", ContentSecurityPolicyForUntrustedContent)
 	builder.WithHeader("Content-Type", "text/plain; charset=utf-8")
 	builder.WithHeader("Cache-Control", "no-cache, max-age=0, must-revalidate, no-store")
 	builder.WithBody(html.EscapeString(err.Error()))
 	builder.Write()
 }
 
-// BadRequest sends a bad request error to the client.
-func BadRequest(w http.ResponseWriter, r *http.Request, err error) {
+// HTMLBadRequest sends a bad request error to the client.
+func HTMLBadRequest(w http.ResponseWriter, r *http.Request, err error) {
 	slog.Warn(http.StatusText(http.StatusBadRequest),
 		slog.Any("error", err),
 		slog.String("client_ip", request.ClientIP(r)),
@@ -60,17 +59,17 @@ func BadRequest(w http.ResponseWriter, r *http.Request, err error) {
 		),
 	)
 
-	builder := response.New(w, r)
+	builder := New(w, r)
 	builder.WithStatus(http.StatusBadRequest)
-	builder.WithHeader("Content-Security-Policy", response.ContentSecurityPolicyForUntrustedContent)
+	builder.WithHeader("Content-Security-Policy", ContentSecurityPolicyForUntrustedContent)
 	builder.WithHeader("Content-Type", "text/plain; charset=utf-8")
 	builder.WithHeader("Cache-Control", "no-cache, max-age=0, must-revalidate, no-store")
 	builder.WithBody(html.EscapeString(err.Error()))
 	builder.Write()
 }
 
-// Forbidden sends a forbidden error to the client.
-func Forbidden(w http.ResponseWriter, r *http.Request) {
+// HTMLForbidden sends a forbidden error to the client.
+func HTMLForbidden(w http.ResponseWriter, r *http.Request) {
 	slog.Warn(http.StatusText(http.StatusForbidden),
 		slog.String("client_ip", request.ClientIP(r)),
 		slog.Group("request",
@@ -83,7 +82,7 @@ func Forbidden(w http.ResponseWriter, r *http.Request) {
 		),
 	)
 
-	builder := response.New(w, r)
+	builder := New(w, r)
 	builder.WithStatus(http.StatusForbidden)
 	builder.WithHeader("Content-Type", "text/html; charset=utf-8")
 	builder.WithHeader("Cache-Control", "no-cache, max-age=0, must-revalidate, no-store")
@@ -91,8 +90,8 @@ func Forbidden(w http.ResponseWriter, r *http.Request) {
 	builder.Write()
 }
 
-// NotFound sends a page not found error to the client.
-func NotFound(w http.ResponseWriter, r *http.Request) {
+// HTMLNotFound sends a page not found error to the client.
+func HTMLNotFound(w http.ResponseWriter, r *http.Request) {
 	slog.Warn(http.StatusText(http.StatusNotFound),
 		slog.String("client_ip", request.ClientIP(r)),
 		slog.Group("request",
@@ -105,7 +104,7 @@ func NotFound(w http.ResponseWriter, r *http.Request) {
 		),
 	)
 
-	builder := response.New(w, r)
+	builder := New(w, r)
 	builder.WithStatus(http.StatusNotFound)
 	builder.WithHeader("Content-Type", "text/html; charset=utf-8")
 	builder.WithHeader("Cache-Control", "no-cache, max-age=0, must-revalidate, no-store")
@@ -113,13 +112,13 @@ func NotFound(w http.ResponseWriter, r *http.Request) {
 	builder.Write()
 }
 
-// Redirect redirects the user to another location.
-func Redirect(w http.ResponseWriter, r *http.Request, uri string) {
+// HTMLRedirect redirects the user to another location.
+func HTMLRedirect(w http.ResponseWriter, r *http.Request, uri string) {
 	http.Redirect(w, r, uri, http.StatusFound)
 }
 
-// RequestedRangeNotSatisfiable sends a range not satisfiable error to the client.
-func RequestedRangeNotSatisfiable(w http.ResponseWriter, r *http.Request, contentRange string) {
+// HTMLRequestedRangeNotSatisfiable sends a range not satisfiable error to the client.
+func HTMLRequestedRangeNotSatisfiable(w http.ResponseWriter, r *http.Request, contentRange string) {
 	slog.Warn(http.StatusText(http.StatusRequestedRangeNotSatisfiable),
 		slog.String("client_ip", request.ClientIP(r)),
 		slog.Group("request",
@@ -132,7 +131,7 @@ func RequestedRangeNotSatisfiable(w http.ResponseWriter, r *http.Request, conten
 		),
 	)
 
-	builder := response.New(w, r)
+	builder := New(w, r)
 	builder.WithStatus(http.StatusRequestedRangeNotSatisfiable)
 	builder.WithHeader("Content-Type", "text/html; charset=utf-8")
 	builder.WithHeader("Cache-Control", "no-cache, max-age=0, must-revalidate, no-store")

+ 0 - 240
internal/http/response/html/html_test.go

@@ -1,240 +0,0 @@
-// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
-// SPDX-License-Identifier: Apache-2.0
-
-package html // import "miniflux.app/v2/internal/http/response/html"
-
-import (
-	"errors"
-	"net/http"
-	"net/http/httptest"
-	"testing"
-)
-
-func TestOKResponse(t *testing.T) {
-	r, err := http.NewRequest("GET", "/", nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	w := httptest.NewRecorder()
-
-	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		OK(w, r, "Some HTML")
-	})
-
-	handler.ServeHTTP(w, r)
-	resp := w.Result()
-
-	expectedStatusCode := http.StatusOK
-	if resp.StatusCode != expectedStatusCode {
-		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, expectedStatusCode)
-	}
-
-	expectedBody := `Some HTML`
-	actualBody := w.Body.String()
-	if actualBody != expectedBody {
-		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, expectedBody)
-	}
-
-	headers := map[string]string{
-		"Content-Type":  "text/html; charset=utf-8",
-		"Cache-Control": "no-cache, max-age=0, must-revalidate, no-store",
-	}
-
-	for header, expected := range headers {
-		actual := resp.Header.Get(header)
-		if actual != expected {
-			t.Fatalf(`Unexpected header value, got %q instead of %q`, actual, expected)
-		}
-	}
-}
-
-func TestServerErrorResponse(t *testing.T) {
-	r, err := http.NewRequest("GET", "/", nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	w := httptest.NewRecorder()
-
-	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		ServerError(w, r, errors.New("Some error with injected HTML <script>alert('XSS')</script>"))
-	})
-
-	handler.ServeHTTP(w, r)
-	resp := w.Result()
-
-	expectedStatusCode := http.StatusInternalServerError
-	if resp.StatusCode != expectedStatusCode {
-		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, expectedStatusCode)
-	}
-
-	expectedBody := `Some error with injected HTML &lt;script&gt;alert(&#39;XSS&#39;)&lt;/script&gt;`
-	actualBody := w.Body.String()
-	if actualBody != expectedBody {
-		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, expectedBody)
-	}
-
-	expectedContentType := "text/plain; charset=utf-8"
-	actualContentType := resp.Header.Get("Content-Type")
-	if actualContentType != expectedContentType {
-		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, expectedContentType)
-	}
-}
-
-func TestBadRequestResponse(t *testing.T) {
-	r, err := http.NewRequest("GET", "/", nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	w := httptest.NewRecorder()
-
-	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		BadRequest(w, r, errors.New("Some error with injected HTML <script>alert('XSS')</script>"))
-	})
-
-	handler.ServeHTTP(w, r)
-	resp := w.Result()
-
-	expectedStatusCode := http.StatusBadRequest
-	if resp.StatusCode != expectedStatusCode {
-		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, expectedStatusCode)
-	}
-
-	expectedBody := `Some error with injected HTML &lt;script&gt;alert(&#39;XSS&#39;)&lt;/script&gt;`
-	actualBody := w.Body.String()
-	if actualBody != expectedBody {
-		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, expectedBody)
-	}
-
-	expectedContentType := "text/plain; charset=utf-8"
-	actualContentType := resp.Header.Get("Content-Type")
-	if actualContentType != expectedContentType {
-		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, expectedContentType)
-	}
-}
-
-func TestForbiddenResponse(t *testing.T) {
-	r, err := http.NewRequest("GET", "/", nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	w := httptest.NewRecorder()
-
-	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		Forbidden(w, r)
-	})
-
-	handler.ServeHTTP(w, r)
-	resp := w.Result()
-
-	expectedStatusCode := http.StatusForbidden
-	if resp.StatusCode != expectedStatusCode {
-		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, expectedStatusCode)
-	}
-
-	expectedBody := `Access Forbidden`
-	actualBody := w.Body.String()
-	if actualBody != expectedBody {
-		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, expectedBody)
-	}
-
-	expectedContentType := "text/html; charset=utf-8"
-	actualContentType := resp.Header.Get("Content-Type")
-	if actualContentType != expectedContentType {
-		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, expectedContentType)
-	}
-}
-
-func TestNotFoundResponse(t *testing.T) {
-	r, err := http.NewRequest("GET", "/", nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	w := httptest.NewRecorder()
-
-	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		NotFound(w, r)
-	})
-
-	handler.ServeHTTP(w, r)
-	resp := w.Result()
-
-	expectedStatusCode := http.StatusNotFound
-	if resp.StatusCode != expectedStatusCode {
-		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, expectedStatusCode)
-	}
-
-	expectedBody := `Page Not Found`
-	actualBody := w.Body.String()
-	if actualBody != expectedBody {
-		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, expectedBody)
-	}
-
-	expectedContentType := "text/html; charset=utf-8"
-	actualContentType := resp.Header.Get("Content-Type")
-	if actualContentType != expectedContentType {
-		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, expectedContentType)
-	}
-}
-
-func TestRedirectResponse(t *testing.T) {
-	r, err := http.NewRequest("GET", "/", nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	w := httptest.NewRecorder()
-
-	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		Redirect(w, r, "/path")
-	})
-
-	handler.ServeHTTP(w, r)
-
-	resp := w.Result()
-	defer resp.Body.Close()
-
-	expectedStatusCode := http.StatusFound
-	if resp.StatusCode != expectedStatusCode {
-		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, expectedStatusCode)
-	}
-
-	expectedResult := "/path"
-	actualResult := resp.Header.Get("Location")
-	if actualResult != expectedResult {
-		t.Fatalf(`Unexpected redirect location, got %q instead of %q`, actualResult, expectedResult)
-	}
-}
-
-func TestRequestedRangeNotSatisfiable(t *testing.T) {
-	r, err := http.NewRequest("GET", "/", nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	w := httptest.NewRecorder()
-
-	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		RequestedRangeNotSatisfiable(w, r, "bytes */12777")
-	})
-
-	handler.ServeHTTP(w, r)
-
-	resp := w.Result()
-	defer resp.Body.Close()
-
-	expectedStatusCode := http.StatusRequestedRangeNotSatisfiable
-	if resp.StatusCode != expectedStatusCode {
-		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, expectedStatusCode)
-	}
-
-	expectedContentRangeHeader := "bytes */12777"
-	actualContentRangeHeader := resp.Header.Get("Content-Range")
-	if actualContentRangeHeader != expectedContentRangeHeader {
-		t.Fatalf(`Unexpected content range header, got %q instead of %q`, actualContentRangeHeader, expectedContentRangeHeader)
-	}
-}

+ 210 - 0
internal/http/response/html_test.go

@@ -0,0 +1,210 @@
+// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package response // import "miniflux.app/v2/internal/http/response"
+
+import (
+	"errors"
+	"net/http"
+	"net/http/httptest"
+	"testing"
+)
+
+func TestHTMLResponse(t *testing.T) {
+	r, err := http.NewRequest("GET", "/", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	w := httptest.NewRecorder()
+
+	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		HTML(w, r, "Some HTML")
+	})
+
+	handler.ServeHTTP(w, r)
+	resp := w.Result()
+
+	if resp.StatusCode != http.StatusOK {
+		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, http.StatusOK)
+	}
+
+	if actualBody := w.Body.String(); actualBody != `Some HTML` {
+		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, `Some HTML`)
+	}
+
+	headers := map[string]string{
+		"Content-Type":  "text/html; charset=utf-8",
+		"Cache-Control": "no-cache, max-age=0, must-revalidate, no-store",
+	}
+
+	for header, expected := range headers {
+		if actual := resp.Header.Get(header); actual != expected {
+			t.Fatalf(`Unexpected header value, got %q instead of %q`, actual, expected)
+		}
+	}
+}
+
+func TestHTMLServerErrorResponse(t *testing.T) {
+	r, err := http.NewRequest("GET", "/", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	w := httptest.NewRecorder()
+
+	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		HTMLServerError(w, r, errors.New("Some error with injected HTML <script>alert('XSS')</script>"))
+	})
+
+	handler.ServeHTTP(w, r)
+	resp := w.Result()
+
+	if resp.StatusCode != http.StatusInternalServerError {
+		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, http.StatusInternalServerError)
+	}
+
+	if actualBody := w.Body.String(); actualBody != `Some error with injected HTML &lt;script&gt;alert(&#39;XSS&#39;)&lt;/script&gt;` {
+		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, `Some error with injected HTML &lt;script&gt;alert(&#39;XSS&#39;)&lt;/script&gt;`)
+	}
+
+	if actualContentType := resp.Header.Get("Content-Type"); actualContentType != "text/plain; charset=utf-8" {
+		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, "text/plain; charset=utf-8")
+	}
+}
+
+func TestHTMLBadRequestResponse(t *testing.T) {
+	r, err := http.NewRequest("GET", "/", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	w := httptest.NewRecorder()
+
+	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		HTMLBadRequest(w, r, errors.New("Some error with injected HTML <script>alert('XSS')</script>"))
+	})
+
+	handler.ServeHTTP(w, r)
+	resp := w.Result()
+
+	if resp.StatusCode != http.StatusBadRequest {
+		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, http.StatusBadRequest)
+	}
+
+	if actualBody := w.Body.String(); actualBody != `Some error with injected HTML &lt;script&gt;alert(&#39;XSS&#39;)&lt;/script&gt;` {
+		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, `Some error with injected HTML &lt;script&gt;alert(&#39;XSS&#39;)&lt;/script&gt;`)
+	}
+
+	if actualContentType := resp.Header.Get("Content-Type"); actualContentType != "text/plain; charset=utf-8" {
+		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, "text/plain; charset=utf-8")
+	}
+}
+
+func TestHTMLForbiddenResponse(t *testing.T) {
+	r, err := http.NewRequest("GET", "/", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	w := httptest.NewRecorder()
+
+	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		HTMLForbidden(w, r)
+	})
+
+	handler.ServeHTTP(w, r)
+	resp := w.Result()
+
+	if resp.StatusCode != http.StatusForbidden {
+		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, http.StatusForbidden)
+	}
+
+	if actualBody := w.Body.String(); actualBody != `Access Forbidden` {
+		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, `Access Forbidden`)
+	}
+
+	if actualContentType := resp.Header.Get("Content-Type"); actualContentType != "text/html; charset=utf-8" {
+		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, "text/html; charset=utf-8")
+	}
+}
+
+func TestHTMLNotFoundResponse(t *testing.T) {
+	r, err := http.NewRequest("GET", "/", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	w := httptest.NewRecorder()
+
+	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		HTMLNotFound(w, r)
+	})
+
+	handler.ServeHTTP(w, r)
+	resp := w.Result()
+
+	if resp.StatusCode != http.StatusNotFound {
+		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, http.StatusNotFound)
+	}
+
+	if actualBody := w.Body.String(); actualBody != `Page Not Found` {
+		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, `Page Not Found`)
+	}
+
+	if actualContentType := resp.Header.Get("Content-Type"); actualContentType != "text/html; charset=utf-8" {
+		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, "text/html; charset=utf-8")
+	}
+}
+
+func TestHTMLRedirectResponse(t *testing.T) {
+	r, err := http.NewRequest("GET", "/", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	w := httptest.NewRecorder()
+
+	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		HTMLRedirect(w, r, "/path")
+	})
+
+	handler.ServeHTTP(w, r)
+
+	resp := w.Result()
+	defer resp.Body.Close()
+
+	if resp.StatusCode != http.StatusFound {
+		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, http.StatusFound)
+	}
+
+	if actualResult := resp.Header.Get("Location"); actualResult != "/path" {
+		t.Fatalf(`Unexpected redirect location, got %q instead of %q`, actualResult, "/path")
+	}
+}
+
+func TestHTMLRequestedRangeNotSatisfiable(t *testing.T) {
+	r, err := http.NewRequest("GET", "/", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	w := httptest.NewRecorder()
+
+	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		HTMLRequestedRangeNotSatisfiable(w, r, "bytes */12777")
+	})
+
+	handler.ServeHTTP(w, r)
+
+	resp := w.Result()
+	defer resp.Body.Close()
+
+	if resp.StatusCode != http.StatusRequestedRangeNotSatisfiable {
+		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, http.StatusRequestedRangeNotSatisfiable)
+	}
+
+	if actualContentRangeHeader := resp.Header.Get("Content-Range"); actualContentRangeHeader != "bytes */12777" {
+		t.Fatalf(`Unexpected content range header, got %q instead of %q`, actualContentRangeHeader, "bytes */12777")
+	}
+}

+ 176 - 0
internal/http/response/json.go

@@ -0,0 +1,176 @@
+// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package response // import "miniflux.app/v2/internal/http/response"
+
+import (
+	"encoding/json"
+	"errors"
+	"log/slog"
+	"net/http"
+
+	"miniflux.app/v2/internal/http/request"
+)
+
+const jsonContentTypeHeader = `application/json`
+
+// JSON creates a new JSON response with a 200 status code.
+func JSON(w http.ResponseWriter, r *http.Request, body any) {
+	responseBody, err := json.Marshal(body)
+	if err != nil {
+		JSONServerError(w, r, err)
+		return
+	}
+
+	builder := New(w, r)
+	builder.WithHeader("Content-Type", jsonContentTypeHeader)
+	builder.WithBody(responseBody)
+	builder.Write()
+}
+
+// JSONCreated sends a created response to the client.
+func JSONCreated(w http.ResponseWriter, r *http.Request, body any) {
+	responseBody, err := json.Marshal(body)
+	if err != nil {
+		JSONServerError(w, r, err)
+		return
+	}
+
+	builder := New(w, r)
+	builder.WithStatus(http.StatusCreated)
+	builder.WithHeader("Content-Type", jsonContentTypeHeader)
+	builder.WithBody(responseBody)
+	builder.Write()
+}
+
+// JSONNoContent sends a no content response to the client.
+func JSONNoContent(w http.ResponseWriter, r *http.Request) {
+	builder := New(w, r)
+	builder.WithStatus(http.StatusNoContent)
+	builder.WithHeader("Content-Type", jsonContentTypeHeader)
+	builder.Write()
+}
+
+// JSONAccepted sends an accepted response to the client.
+func JSONAccepted(w http.ResponseWriter, r *http.Request) {
+	builder := New(w, r)
+	builder.WithStatus(http.StatusAccepted)
+	builder.WithHeader("Content-Type", jsonContentTypeHeader)
+	builder.Write()
+}
+
+// JSONServerError sends an internal error to the client.
+func JSONServerError(w http.ResponseWriter, r *http.Request, err error) {
+	slog.Error(http.StatusText(http.StatusInternalServerError),
+		slog.Any("error", err),
+		slog.String("client_ip", request.ClientIP(r)),
+		slog.Group("request",
+			slog.String("method", r.Method),
+			slog.String("uri", r.RequestURI),
+			slog.String("user_agent", r.UserAgent()),
+		),
+		slog.Group("response",
+			slog.Int("status_code", http.StatusInternalServerError),
+		),
+	)
+
+	builder := New(w, r)
+	builder.WithStatus(http.StatusInternalServerError)
+	builder.WithHeader("Content-Type", jsonContentTypeHeader)
+	builder.WithBody(generateJSONError(err))
+	builder.Write()
+}
+
+// JSONBadRequest sends a bad request error to the client.
+func JSONBadRequest(w http.ResponseWriter, r *http.Request, err error) {
+	slog.Warn(http.StatusText(http.StatusBadRequest),
+		slog.Any("error", err),
+		slog.String("client_ip", request.ClientIP(r)),
+		slog.Group("request",
+			slog.String("method", r.Method),
+			slog.String("uri", r.RequestURI),
+			slog.String("user_agent", r.UserAgent()),
+		),
+		slog.Group("response",
+			slog.Int("status_code", http.StatusBadRequest),
+		),
+	)
+
+	builder := New(w, r)
+	builder.WithStatus(http.StatusBadRequest)
+	builder.WithHeader("Content-Type", jsonContentTypeHeader)
+	builder.WithBody(generateJSONError(err))
+	builder.Write()
+}
+
+// JSONUnauthorized sends a not authorized error to the client.
+func JSONUnauthorized(w http.ResponseWriter, r *http.Request) {
+	slog.Warn(http.StatusText(http.StatusUnauthorized),
+		slog.String("client_ip", request.ClientIP(r)),
+		slog.Group("request",
+			slog.String("method", r.Method),
+			slog.String("uri", r.RequestURI),
+			slog.String("user_agent", r.UserAgent()),
+		),
+		slog.Group("response",
+			slog.Int("status_code", http.StatusUnauthorized),
+		),
+	)
+
+	builder := New(w, r)
+	builder.WithStatus(http.StatusUnauthorized)
+	builder.WithHeader("Content-Type", jsonContentTypeHeader)
+	builder.WithBody(generateJSONError(errors.New("access unauthorized")))
+	builder.Write()
+}
+
+// JSONForbidden sends a forbidden error to the client.
+func JSONForbidden(w http.ResponseWriter, r *http.Request) {
+	slog.Warn(http.StatusText(http.StatusForbidden),
+		slog.String("client_ip", request.ClientIP(r)),
+		slog.Group("request",
+			slog.String("method", r.Method),
+			slog.String("uri", r.RequestURI),
+			slog.String("user_agent", r.UserAgent()),
+		),
+		slog.Group("response",
+			slog.Int("status_code", http.StatusForbidden),
+		),
+	)
+
+	builder := New(w, r)
+	builder.WithStatus(http.StatusForbidden)
+	builder.WithHeader("Content-Type", jsonContentTypeHeader)
+	builder.WithBody(generateJSONError(errors.New("access forbidden")))
+	builder.Write()
+}
+
+// JSONNotFound sends a page not found error to the client.
+func JSONNotFound(w http.ResponseWriter, r *http.Request) {
+	slog.Warn(http.StatusText(http.StatusNotFound),
+		slog.String("client_ip", request.ClientIP(r)),
+		slog.Group("request",
+			slog.String("method", r.Method),
+			slog.String("uri", r.RequestURI),
+			slog.String("user_agent", r.UserAgent()),
+		),
+		slog.Group("response",
+			slog.Int("status_code", http.StatusNotFound),
+		),
+	)
+
+	builder := New(w, r)
+	builder.WithStatus(http.StatusNotFound)
+	builder.WithHeader("Content-Type", jsonContentTypeHeader)
+	builder.WithBody(generateJSONError(errors.New("resource not found")))
+	builder.Write()
+}
+
+func generateJSONError(err error) []byte {
+	type errorMsg struct {
+		ErrorMessage string `json:"error_message"`
+	}
+
+	encodedBody, _ := json.Marshal(errorMsg{ErrorMessage: err.Error()})
+	return encodedBody
+}

+ 0 - 215
internal/http/response/json/json.go

@@ -1,215 +0,0 @@
-// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
-// SPDX-License-Identifier: Apache-2.0
-
-package json // import "miniflux.app/v2/internal/http/response/json"
-
-import (
-	"encoding/json"
-	"errors"
-	"log/slog"
-	"net/http"
-
-	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response"
-)
-
-const contentTypeHeader = `application/json`
-
-// OK creates a new JSON response with a 200 status code.
-func OK(w http.ResponseWriter, r *http.Request, body any) {
-	responseBody, err := json.Marshal(body)
-	if err != nil {
-		ServerError(w, r, err)
-		return
-	}
-
-	builder := response.New(w, r)
-	builder.WithHeader("Content-Type", contentTypeHeader)
-	builder.WithBody(responseBody)
-	builder.Write()
-}
-
-// Created sends a created response to the client.
-func Created(w http.ResponseWriter, r *http.Request, body any) {
-	responseBody, err := json.Marshal(body)
-	if err != nil {
-		ServerError(w, r, err)
-		return
-	}
-
-	builder := response.New(w, r)
-	builder.WithStatus(http.StatusCreated)
-	builder.WithHeader("Content-Type", contentTypeHeader)
-	builder.WithBody(responseBody)
-	builder.Write()
-}
-
-// NoContent sends a no content response to the client.
-func NoContent(w http.ResponseWriter, r *http.Request) {
-	builder := response.New(w, r)
-	builder.WithStatus(http.StatusNoContent)
-	builder.WithHeader("Content-Type", contentTypeHeader)
-	builder.Write()
-}
-
-func Accepted(w http.ResponseWriter, r *http.Request) {
-	builder := response.New(w, r)
-	builder.WithStatus(http.StatusAccepted)
-	builder.WithHeader("Content-Type", contentTypeHeader)
-	builder.Write()
-}
-
-// ServerError sends an internal error to the client.
-func ServerError(w http.ResponseWriter, r *http.Request, err error) {
-	slog.Error(http.StatusText(http.StatusInternalServerError),
-		slog.Any("error", err),
-		slog.String("client_ip", request.ClientIP(r)),
-		slog.Group("request",
-			slog.String("method", r.Method),
-			slog.String("uri", r.RequestURI),
-			slog.String("user_agent", r.UserAgent()),
-		),
-		slog.Group("response",
-			slog.Int("status_code", http.StatusInternalServerError),
-		),
-	)
-
-	responseBody, jsonErr := generateJSONError(err)
-	if jsonErr != nil {
-		slog.Error("Unable to generate JSON error", slog.Any("error", jsonErr))
-		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
-		return
-	}
-
-	builder := response.New(w, r)
-	builder.WithStatus(http.StatusInternalServerError)
-	builder.WithHeader("Content-Type", contentTypeHeader)
-	builder.WithBody(responseBody)
-	builder.Write()
-}
-
-// BadRequest sends a bad request error to the client.
-func BadRequest(w http.ResponseWriter, r *http.Request, err error) {
-	slog.Warn(http.StatusText(http.StatusBadRequest),
-		slog.Any("error", err),
-		slog.String("client_ip", request.ClientIP(r)),
-		slog.Group("request",
-			slog.String("method", r.Method),
-			slog.String("uri", r.RequestURI),
-			slog.String("user_agent", r.UserAgent()),
-		),
-		slog.Group("response",
-			slog.Int("status_code", http.StatusBadRequest),
-		),
-	)
-
-	responseBody, jsonErr := generateJSONError(err)
-	if jsonErr != nil {
-		slog.Error("Unable to generate JSON error", slog.Any("error", jsonErr))
-		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
-		return
-	}
-
-	builder := response.New(w, r)
-	builder.WithStatus(http.StatusBadRequest)
-	builder.WithHeader("Content-Type", contentTypeHeader)
-	builder.WithBody(responseBody)
-	builder.Write()
-}
-
-// Unauthorized sends a not authorized error to the client.
-func Unauthorized(w http.ResponseWriter, r *http.Request) {
-	slog.Warn(http.StatusText(http.StatusUnauthorized),
-		slog.String("client_ip", request.ClientIP(r)),
-		slog.Group("request",
-			slog.String("method", r.Method),
-			slog.String("uri", r.RequestURI),
-			slog.String("user_agent", r.UserAgent()),
-		),
-		slog.Group("response",
-			slog.Int("status_code", http.StatusUnauthorized),
-		),
-	)
-
-	responseBody, jsonErr := generateJSONError(errors.New("access unauthorized"))
-	if jsonErr != nil {
-		slog.Error("Unable to generate JSON error", slog.Any("error", jsonErr))
-		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
-		return
-	}
-
-	builder := response.New(w, r)
-	builder.WithStatus(http.StatusUnauthorized)
-	builder.WithHeader("Content-Type", contentTypeHeader)
-	builder.WithBody(responseBody)
-	builder.Write()
-}
-
-// Forbidden sends a forbidden error to the client.
-func Forbidden(w http.ResponseWriter, r *http.Request) {
-	slog.Warn(http.StatusText(http.StatusForbidden),
-		slog.String("client_ip", request.ClientIP(r)),
-		slog.Group("request",
-			slog.String("method", r.Method),
-			slog.String("uri", r.RequestURI),
-			slog.String("user_agent", r.UserAgent()),
-		),
-		slog.Group("response",
-			slog.Int("status_code", http.StatusForbidden),
-		),
-	)
-
-	responseBody, jsonErr := generateJSONError(errors.New("access forbidden"))
-	if jsonErr != nil {
-		slog.Error("Unable to generate JSON error", slog.Any("error", jsonErr))
-		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
-		return
-	}
-
-	builder := response.New(w, r)
-	builder.WithStatus(http.StatusForbidden)
-	builder.WithHeader("Content-Type", contentTypeHeader)
-	builder.WithBody(responseBody)
-	builder.Write()
-}
-
-// NotFound sends a page not found error to the client.
-func NotFound(w http.ResponseWriter, r *http.Request) {
-	slog.Warn(http.StatusText(http.StatusNotFound),
-		slog.String("client_ip", request.ClientIP(r)),
-		slog.Group("request",
-			slog.String("method", r.Method),
-			slog.String("uri", r.RequestURI),
-			slog.String("user_agent", r.UserAgent()),
-		),
-		slog.Group("response",
-			slog.Int("status_code", http.StatusNotFound),
-		),
-	)
-
-	responseBody, jsonErr := generateJSONError(errors.New("resource not found"))
-	if jsonErr != nil {
-		slog.Error("Unable to generate JSON error", slog.Any("error", jsonErr))
-		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
-		return
-	}
-
-	builder := response.New(w, r)
-	builder.WithStatus(http.StatusNotFound)
-	builder.WithHeader("Content-Type", contentTypeHeader)
-	builder.WithBody(responseBody)
-	builder.Write()
-}
-
-func generateJSONError(err error) ([]byte, error) {
-	type errorMsg struct {
-		ErrorMessage string `json:"error_message"`
-	}
-
-	encodedBody, err := json.Marshal(errorMsg{ErrorMessage: err.Error()})
-	if err != nil {
-		return nil, err
-	}
-
-	return encodedBody, nil
-}

+ 0 - 312
internal/http/response/json/json_test.go

@@ -1,312 +0,0 @@
-// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
-// SPDX-License-Identifier: Apache-2.0
-
-package json // import "miniflux.app/v2/internal/http/response/json"
-
-import (
-	"errors"
-	"net/http"
-	"net/http/httptest"
-	"testing"
-)
-
-func TestOKResponse(t *testing.T) {
-	r, err := http.NewRequest("GET", "/", nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	w := httptest.NewRecorder()
-
-	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		OK(w, r, map[string]string{"key": "value"})
-	})
-
-	handler.ServeHTTP(w, r)
-
-	resp := w.Result()
-	defer resp.Body.Close()
-
-	expectedStatusCode := http.StatusOK
-	if resp.StatusCode != expectedStatusCode {
-		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, expectedStatusCode)
-	}
-
-	expectedBody := `{"key":"value"}`
-	actualBody := w.Body.String()
-	if actualBody != expectedBody {
-		t.Fatalf(`Unexpected body, got %q instead of %q`, actualBody, expectedBody)
-	}
-
-	expectedContentType := contentTypeHeader
-	actualContentType := resp.Header.Get("Content-Type")
-	if actualContentType != expectedContentType {
-		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, expectedContentType)
-	}
-}
-
-func TestCreatedResponse(t *testing.T) {
-	r, err := http.NewRequest("GET", "/", nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	w := httptest.NewRecorder()
-
-	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		Created(w, r, map[string]string{"key": "value"})
-	})
-
-	handler.ServeHTTP(w, r)
-	resp := w.Result()
-
-	expectedStatusCode := http.StatusCreated
-	if resp.StatusCode != expectedStatusCode {
-		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, expectedStatusCode)
-	}
-
-	expectedBody := `{"key":"value"}`
-	actualBody := w.Body.String()
-	if actualBody != expectedBody {
-		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, expectedBody)
-	}
-
-	expectedContentType := contentTypeHeader
-	actualContentType := resp.Header.Get("Content-Type")
-	if actualContentType != expectedContentType {
-		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, expectedContentType)
-	}
-}
-
-func TestNoContentResponse(t *testing.T) {
-	r, err := http.NewRequest("GET", "/", nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	w := httptest.NewRecorder()
-
-	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		NoContent(w, r)
-	})
-
-	handler.ServeHTTP(w, r)
-	resp := w.Result()
-
-	expectedStatusCode := http.StatusNoContent
-	if resp.StatusCode != expectedStatusCode {
-		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, expectedStatusCode)
-	}
-
-	expectedBody := ``
-	actualBody := w.Body.String()
-	if actualBody != expectedBody {
-		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, expectedBody)
-	}
-
-	expectedContentType := contentTypeHeader
-	actualContentType := resp.Header.Get("Content-Type")
-	if actualContentType != expectedContentType {
-		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, expectedContentType)
-	}
-}
-
-func TestServerErrorResponse(t *testing.T) {
-	r, err := http.NewRequest("GET", "/", nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	w := httptest.NewRecorder()
-
-	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		ServerError(w, r, errors.New("some error"))
-	})
-
-	handler.ServeHTTP(w, r)
-
-	resp := w.Result()
-	defer resp.Body.Close()
-
-	expectedStatusCode := http.StatusInternalServerError
-	if resp.StatusCode != expectedStatusCode {
-		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, expectedStatusCode)
-	}
-
-	expectedBody := `{"error_message":"some error"}`
-	actualBody := w.Body.String()
-	if actualBody != expectedBody {
-		t.Fatalf(`Unexpected body, got %q instead of %q`, actualBody, expectedBody)
-	}
-
-	expectedContentType := contentTypeHeader
-	actualContentType := resp.Header.Get("Content-Type")
-	if actualContentType != expectedContentType {
-		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, expectedContentType)
-	}
-}
-
-func TestBadRequestResponse(t *testing.T) {
-	r, err := http.NewRequest("GET", "/", nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	w := httptest.NewRecorder()
-
-	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		BadRequest(w, r, errors.New("Some Error"))
-	})
-
-	handler.ServeHTTP(w, r)
-	resp := w.Result()
-
-	expectedStatusCode := http.StatusBadRequest
-	if resp.StatusCode != expectedStatusCode {
-		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, expectedStatusCode)
-	}
-
-	expectedBody := `{"error_message":"Some Error"}`
-	actualBody := w.Body.String()
-	if actualBody != expectedBody {
-		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, expectedBody)
-	}
-
-	expectedContentType := contentTypeHeader
-	actualContentType := resp.Header.Get("Content-Type")
-	if actualContentType != expectedContentType {
-		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, expectedContentType)
-	}
-}
-
-func TestUnauthorizedResponse(t *testing.T) {
-	r, err := http.NewRequest("GET", "/", nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	w := httptest.NewRecorder()
-
-	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		Unauthorized(w, r)
-	})
-
-	handler.ServeHTTP(w, r)
-	resp := w.Result()
-
-	expectedStatusCode := http.StatusUnauthorized
-	if resp.StatusCode != expectedStatusCode {
-		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, expectedStatusCode)
-	}
-
-	expectedBody := `{"error_message":"access unauthorized"}`
-	actualBody := w.Body.String()
-	if actualBody != expectedBody {
-		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, expectedBody)
-	}
-
-	expectedContentType := contentTypeHeader
-	actualContentType := resp.Header.Get("Content-Type")
-	if actualContentType != expectedContentType {
-		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, expectedContentType)
-	}
-}
-
-func TestForbiddenResponse(t *testing.T) {
-	r, err := http.NewRequest("GET", "/", nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	w := httptest.NewRecorder()
-
-	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		Forbidden(w, r)
-	})
-
-	handler.ServeHTTP(w, r)
-	resp := w.Result()
-
-	expectedStatusCode := http.StatusForbidden
-	if resp.StatusCode != expectedStatusCode {
-		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, expectedStatusCode)
-	}
-
-	expectedBody := `{"error_message":"access forbidden"}`
-	actualBody := w.Body.String()
-	if actualBody != expectedBody {
-		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, expectedBody)
-	}
-
-	expectedContentType := contentTypeHeader
-	actualContentType := resp.Header.Get("Content-Type")
-	if actualContentType != expectedContentType {
-		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, expectedContentType)
-	}
-}
-
-func TestNotFoundResponse(t *testing.T) {
-	r, err := http.NewRequest("GET", "/", nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	w := httptest.NewRecorder()
-
-	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		NotFound(w, r)
-	})
-
-	handler.ServeHTTP(w, r)
-	resp := w.Result()
-
-	expectedStatusCode := http.StatusNotFound
-	if resp.StatusCode != expectedStatusCode {
-		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, expectedStatusCode)
-	}
-
-	expectedBody := `{"error_message":"resource not found"}`
-	actualBody := w.Body.String()
-	if actualBody != expectedBody {
-		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, expectedBody)
-	}
-
-	expectedContentType := contentTypeHeader
-	actualContentType := resp.Header.Get("Content-Type")
-	if actualContentType != expectedContentType {
-		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, expectedContentType)
-	}
-}
-
-func TestBuildInvalidJSONResponse(t *testing.T) {
-	r, err := http.NewRequest("GET", "/", nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	w := httptest.NewRecorder()
-
-	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		OK(w, r, make(chan int))
-	})
-
-	handler.ServeHTTP(w, r)
-	resp := w.Result()
-
-	expectedStatusCode := http.StatusInternalServerError
-	if resp.StatusCode != expectedStatusCode {
-		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, expectedStatusCode)
-	}
-
-	expectedBody := `{"error_message":"json: unsupported type: chan int"}`
-	actualBody := w.Body.String()
-	if actualBody != expectedBody {
-		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, expectedBody)
-	}
-
-	expectedContentType := contentTypeHeader
-	actualContentType := resp.Header.Get("Content-Type")
-	if actualContentType != expectedContentType {
-		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, expectedContentType)
-	}
-}

+ 330 - 0
internal/http/response/json_test.go

@@ -0,0 +1,330 @@
+// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package response // import "miniflux.app/v2/internal/http/response"
+
+import (
+	"errors"
+	"net/http"
+	"net/http/httptest"
+	"testing"
+)
+
+func TestJSONResponse(t *testing.T) {
+	r, err := http.NewRequest("GET", "/", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	w := httptest.NewRecorder()
+
+	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		JSON(w, r, map[string]string{"key": "value"})
+	})
+
+	handler.ServeHTTP(w, r)
+
+	resp := w.Result()
+	defer resp.Body.Close()
+
+	if resp.StatusCode != http.StatusOK {
+		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, http.StatusOK)
+	}
+
+	if actualBody := w.Body.String(); actualBody != `{"key":"value"}` {
+		t.Fatalf(`Unexpected body, got %q instead of %q`, actualBody, `{"key":"value"}`)
+	}
+
+	if actualContentType := resp.Header.Get("Content-Type"); actualContentType != jsonContentTypeHeader {
+		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, jsonContentTypeHeader)
+	}
+}
+
+func TestJSONCreatedResponse(t *testing.T) {
+	r, err := http.NewRequest("GET", "/", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	w := httptest.NewRecorder()
+
+	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		JSONCreated(w, r, map[string]string{"key": "value"})
+	})
+
+	handler.ServeHTTP(w, r)
+	resp := w.Result()
+
+	if resp.StatusCode != http.StatusCreated {
+		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, http.StatusCreated)
+	}
+
+	if actualBody := w.Body.String(); actualBody != `{"key":"value"}` {
+		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, `{"key":"value"}`)
+	}
+
+	if actualContentType := resp.Header.Get("Content-Type"); actualContentType != jsonContentTypeHeader {
+		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, jsonContentTypeHeader)
+	}
+}
+
+func TestJSONNoContentResponse(t *testing.T) {
+	r, err := http.NewRequest("GET", "/", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	w := httptest.NewRecorder()
+
+	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		JSONNoContent(w, r)
+	})
+
+	handler.ServeHTTP(w, r)
+	resp := w.Result()
+
+	if resp.StatusCode != http.StatusNoContent {
+		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, http.StatusNoContent)
+	}
+
+	if actualBody := w.Body.String(); actualBody != `` {
+		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, ``)
+	}
+
+	if actualContentType := resp.Header.Get("Content-Type"); actualContentType != jsonContentTypeHeader {
+		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, jsonContentTypeHeader)
+	}
+}
+
+func TestJSONAcceptedResponse(t *testing.T) {
+	r, err := http.NewRequest("GET", "/", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	w := httptest.NewRecorder()
+
+	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		JSONAccepted(w, r)
+	})
+
+	handler.ServeHTTP(w, r)
+	resp := w.Result()
+
+	if resp.StatusCode != http.StatusAccepted {
+		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, http.StatusAccepted)
+	}
+
+	if actualBody := w.Body.String(); actualBody != `` {
+		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, ``)
+	}
+
+	if actualContentType := resp.Header.Get("Content-Type"); actualContentType != jsonContentTypeHeader {
+		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, jsonContentTypeHeader)
+	}
+}
+
+func TestJSONServerErrorResponse(t *testing.T) {
+	r, err := http.NewRequest("GET", "/", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	w := httptest.NewRecorder()
+
+	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		JSONServerError(w, r, errors.New("some error"))
+	})
+
+	handler.ServeHTTP(w, r)
+
+	resp := w.Result()
+	defer resp.Body.Close()
+
+	if resp.StatusCode != http.StatusInternalServerError {
+		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, http.StatusInternalServerError)
+	}
+
+	if actualBody := w.Body.String(); actualBody != `{"error_message":"some error"}` {
+		t.Fatalf(`Unexpected body, got %q instead of %q`, actualBody, `{"error_message":"some error"}`)
+	}
+
+	if actualContentType := resp.Header.Get("Content-Type"); actualContentType != jsonContentTypeHeader {
+		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, jsonContentTypeHeader)
+	}
+}
+
+func TestJSONBadRequestResponse(t *testing.T) {
+	r, err := http.NewRequest("GET", "/", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	w := httptest.NewRecorder()
+
+	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		JSONBadRequest(w, r, errors.New("Some Error"))
+	})
+
+	handler.ServeHTTP(w, r)
+	resp := w.Result()
+
+	if resp.StatusCode != http.StatusBadRequest {
+		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, http.StatusBadRequest)
+	}
+
+	if actualBody := w.Body.String(); actualBody != `{"error_message":"Some Error"}` {
+		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, `{"error_message":"Some Error"}`)
+	}
+
+	if actualContentType := resp.Header.Get("Content-Type"); actualContentType != jsonContentTypeHeader {
+		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, jsonContentTypeHeader)
+	}
+}
+
+func TestJSONUnauthorizedResponse(t *testing.T) {
+	r, err := http.NewRequest("GET", "/", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	w := httptest.NewRecorder()
+
+	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		JSONUnauthorized(w, r)
+	})
+
+	handler.ServeHTTP(w, r)
+	resp := w.Result()
+
+	if resp.StatusCode != http.StatusUnauthorized {
+		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, http.StatusUnauthorized)
+	}
+
+	if actualBody := w.Body.String(); actualBody != `{"error_message":"access unauthorized"}` {
+		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, `{"error_message":"access unauthorized"}`)
+	}
+
+	if actualContentType := resp.Header.Get("Content-Type"); actualContentType != jsonContentTypeHeader {
+		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, jsonContentTypeHeader)
+	}
+}
+
+func TestJSONForbiddenResponse(t *testing.T) {
+	r, err := http.NewRequest("GET", "/", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	w := httptest.NewRecorder()
+
+	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		JSONForbidden(w, r)
+	})
+
+	handler.ServeHTTP(w, r)
+	resp := w.Result()
+
+	if resp.StatusCode != http.StatusForbidden {
+		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, http.StatusForbidden)
+	}
+
+	if actualBody := w.Body.String(); actualBody != `{"error_message":"access forbidden"}` {
+		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, `{"error_message":"access forbidden"}`)
+	}
+
+	if actualContentType := resp.Header.Get("Content-Type"); actualContentType != jsonContentTypeHeader {
+		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, jsonContentTypeHeader)
+	}
+}
+
+func TestJSONNotFoundResponse(t *testing.T) {
+	r, err := http.NewRequest("GET", "/", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	w := httptest.NewRecorder()
+
+	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		JSONNotFound(w, r)
+	})
+
+	handler.ServeHTTP(w, r)
+	resp := w.Result()
+
+	if resp.StatusCode != http.StatusNotFound {
+		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, http.StatusNotFound)
+	}
+
+	if actualBody := w.Body.String(); actualBody != `{"error_message":"resource not found"}` {
+		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, `{"error_message":"resource not found"}`)
+	}
+
+	if actualContentType := resp.Header.Get("Content-Type"); actualContentType != jsonContentTypeHeader {
+		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, jsonContentTypeHeader)
+	}
+}
+
+func TestBuildInvalidJSONResponse(t *testing.T) {
+	r, err := http.NewRequest("GET", "/", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	w := httptest.NewRecorder()
+
+	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		JSON(w, r, make(chan int))
+	})
+
+	handler.ServeHTTP(w, r)
+	resp := w.Result()
+
+	if resp.StatusCode != http.StatusInternalServerError {
+		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, http.StatusInternalServerError)
+	}
+
+	if actualBody := w.Body.String(); actualBody != `{"error_message":"json: unsupported type: chan int"}` {
+		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, `{"error_message":"json: unsupported type: chan int"}`)
+	}
+
+	if actualContentType := resp.Header.Get("Content-Type"); actualContentType != jsonContentTypeHeader {
+		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, jsonContentTypeHeader)
+	}
+}
+
+func TestBuildInvalidJSONCreatedResponse(t *testing.T) {
+	r, err := http.NewRequest("GET", "/", nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	w := httptest.NewRecorder()
+
+	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		JSONCreated(w, r, make(chan int))
+	})
+
+	handler.ServeHTTP(w, r)
+	resp := w.Result()
+
+	if resp.StatusCode != http.StatusInternalServerError {
+		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, http.StatusInternalServerError)
+	}
+
+	if actualBody := w.Body.String(); actualBody != `{"error_message":"json: unsupported type: chan int"}` {
+		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, `{"error_message":"json: unsupported type: chan int"}`)
+	}
+
+	if actualContentType := resp.Header.Get("Content-Type"); actualContentType != jsonContentTypeHeader {
+		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, jsonContentTypeHeader)
+	}
+}
+
+func TestGenerateJSONError(t *testing.T) {
+	actualBody := string(generateJSONError(errors.New("some error")))
+	if actualBody != `{"error_message":"some error"}` {
+		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, `{"error_message":"some error"}`)
+	}
+}

+ 23 - 0
internal/http/response/xml.go

@@ -0,0 +1,23 @@
+// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package response // import "miniflux.app/v2/internal/http/response"
+
+import "net/http"
+
+// XML writes a standard XML response with a status 200 OK.
+func XML[T []byte | string](w http.ResponseWriter, r *http.Request, body T) {
+	builder := New(w, r)
+	builder.WithHeader("Content-Type", "text/xml; charset=utf-8")
+	builder.WithBody(body)
+	builder.Write()
+}
+
+// XMLAttachment forces the XML document to be downloaded by the web browser.
+func XMLAttachment[T []byte | string](w http.ResponseWriter, r *http.Request, filename string, body T) {
+	builder := New(w, r)
+	builder.WithHeader("Content-Type", "text/xml; charset=utf-8")
+	builder.WithAttachment(filename)
+	builder.WithBody(body)
+	builder.Write()
+}

+ 0 - 27
internal/http/response/xml/xml.go

@@ -1,27 +0,0 @@
-// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
-// SPDX-License-Identifier: Apache-2.0
-
-package xml // import "miniflux.app/v2/internal/http/response/xml"
-
-import (
-	"net/http"
-
-	"miniflux.app/v2/internal/http/response"
-)
-
-// OK writes a standard XML response with a status 200 OK.
-func OK[T []byte | string](w http.ResponseWriter, r *http.Request, body T) {
-	builder := response.New(w, r)
-	builder.WithHeader("Content-Type", "text/xml; charset=utf-8")
-	builder.WithBody(body)
-	builder.Write()
-}
-
-// Attachment forces the XML document to be downloaded by the web browser.
-func Attachment[T []byte | string](w http.ResponseWriter, r *http.Request, filename string, body T) {
-	builder := response.New(w, r)
-	builder.WithHeader("Content-Type", "text/xml; charset=utf-8")
-	builder.WithAttachment(filename)
-	builder.WithBody(body)
-	builder.Write()
-}

+ 16 - 25
internal/http/response/xml/xml_test.go → internal/http/response/xml_test.go

@@ -1,7 +1,7 @@
 // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
 // SPDX-License-Identifier: Apache-2.0
 
-package xml // import "miniflux.app/v2/internal/http/response/xml"
+package response // import "miniflux.app/v2/internal/http/response"
 
 import (
 	"net/http"
@@ -9,7 +9,7 @@ import (
 	"testing"
 )
 
-func TestOKResponse(t *testing.T) {
+func TestXMLResponse(t *testing.T) {
 	r, err := http.NewRequest("GET", "/", nil)
 	if err != nil {
 		t.Fatal(err)
@@ -18,31 +18,26 @@ func TestOKResponse(t *testing.T) {
 	w := httptest.NewRecorder()
 
 	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		OK(w, r, "Some XML")
+		XML(w, r, "Some XML")
 	})
 
 	handler.ServeHTTP(w, r)
 	resp := w.Result()
 
-	expectedStatusCode := http.StatusOK
-	if resp.StatusCode != expectedStatusCode {
-		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, expectedStatusCode)
+	if resp.StatusCode != http.StatusOK {
+		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, http.StatusOK)
 	}
 
-	expectedBody := `Some XML`
-	actualBody := w.Body.String()
-	if actualBody != expectedBody {
-		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, expectedBody)
+	if actualBody := w.Body.String(); actualBody != "Some XML" {
+		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, "Some XML")
 	}
 
-	expectedContentType := "text/xml; charset=utf-8"
-	actualContentType := resp.Header.Get("Content-Type")
-	if actualContentType != expectedContentType {
-		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, expectedContentType)
+	if actualContentType := resp.Header.Get("Content-Type"); actualContentType != "text/xml; charset=utf-8" {
+		t.Fatalf(`Unexpected content type, got %q instead of %q`, actualContentType, "text/xml; charset=utf-8")
 	}
 }
 
-func TestAttachmentResponse(t *testing.T) {
+func TestXMLAttachmentResponse(t *testing.T) {
 	r, err := http.NewRequest("GET", "/", nil)
 	if err != nil {
 		t.Fatal(err)
@@ -51,21 +46,18 @@ func TestAttachmentResponse(t *testing.T) {
 	w := httptest.NewRecorder()
 
 	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		Attachment(w, r, "file.xml", "Some XML")
+		XMLAttachment(w, r, "file.xml", "Some XML")
 	})
 
 	handler.ServeHTTP(w, r)
 	resp := w.Result()
 
-	expectedStatusCode := http.StatusOK
-	if resp.StatusCode != expectedStatusCode {
-		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, expectedStatusCode)
+	if resp.StatusCode != http.StatusOK {
+		t.Fatalf(`Unexpected status code, got %d instead of %d`, resp.StatusCode, http.StatusOK)
 	}
 
-	expectedBody := `Some XML`
-	actualBody := w.Body.String()
-	if actualBody != expectedBody {
-		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, expectedBody)
+	if actualBody := w.Body.String(); actualBody != "Some XML" {
+		t.Fatalf(`Unexpected body, got %s instead of %s`, actualBody, "Some XML")
 	}
 
 	headers := map[string]string{
@@ -74,8 +66,7 @@ func TestAttachmentResponse(t *testing.T) {
 	}
 
 	for header, expected := range headers {
-		actual := resp.Header.Get(header)
-		if actual != expected {
+		if actual := resp.Header.Get(header); actual != expected {
 			t.Fatalf(`Unexpected header value, got %q instead of %q`, actual, expected)
 		}
 	}

+ 3 - 3
internal/ui/about.go

@@ -9,7 +9,7 @@ import (
 
 	"miniflux.app/v2/internal/config"
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/ui/session"
 	"miniflux.app/v2/internal/ui/view"
 	"miniflux.app/v2/internal/version"
@@ -18,7 +18,7 @@ import (
 func (h *handler) showAboutPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -43,5 +43,5 @@ func (h *handler) showAboutPage(w http.ResponseWriter, r *http.Request) {
 		view.Set("db_usage", dbSize)
 	}
 
-	html.OK(w, r, view.Render("about"))
+	response.HTML(w, r, view.Render("about"))
 }

+ 3 - 3
internal/ui/api_key_create.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/ui/form"
 	"miniflux.app/v2/internal/ui/session"
 	"miniflux.app/v2/internal/ui/view"
@@ -16,7 +16,7 @@ import (
 func (h *handler) showCreateAPIKeyPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -28,5 +28,5 @@ func (h *handler) showCreateAPIKeyPage(w http.ResponseWriter, r *http.Request) {
 	view.Set("countUnread", h.store.CountUnreadEntries(user.ID))
 	view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(user.ID))
 
-	html.OK(w, r, view.Render("create_api_key"))
+	response.HTML(w, r, view.Render("create_api_key"))
 }

+ 4 - 4
internal/ui/api_key_list.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/ui/session"
 	"miniflux.app/v2/internal/ui/view"
 )
@@ -15,13 +15,13 @@ import (
 func (h *handler) showAPIKeysPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	apiKeys, err := h.store.APIKeys(user.ID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -33,5 +33,5 @@ func (h *handler) showAPIKeysPage(w http.ResponseWriter, r *http.Request) {
 	view.Set("countUnread", h.store.CountUnreadEntries(user.ID))
 	view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(user.ID))
 
-	html.OK(w, r, view.Render("api_keys"))
+	response.HTML(w, r, view.Render("api_keys"))
 }

+ 3 - 3
internal/ui/api_key_remove.go

@@ -7,16 +7,16 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 )
 
 func (h *handler) deleteAPIKey(w http.ResponseWriter, r *http.Request) {
 	keyID := request.RouteInt64Param(r, "keyID")
 	if err := h.store.DeleteAPIKey(request.UserID(r), keyID); err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
-	html.Redirect(w, r, route.Path(h.router, "apiKeys"))
+	response.HTMLRedirect(w, r, route.Path(h.router, "apiKeys"))
 }

+ 5 - 5
internal/ui/api_key_save.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/ui/form"
@@ -19,7 +19,7 @@ import (
 func (h *handler) saveAPIKey(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -37,14 +37,14 @@ func (h *handler) saveAPIKey(w http.ResponseWriter, r *http.Request) {
 		view.Set("countUnread", h.store.CountUnreadEntries(user.ID))
 		view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(user.ID))
 		view.Set("errorMessage", validationErr.Translate(user.Language))
-		html.OK(w, r, view.Render("create_api_key"))
+		response.HTML(w, r, view.Render("create_api_key"))
 		return
 	}
 
 	if _, err = h.store.CreateAPIKey(user.ID, apiKeyCreationRequest.Description); err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
-	html.Redirect(w, r, route.Path(h.router, "apiKeys"))
+	response.HTMLRedirect(w, r, route.Path(h.router, "apiKeys"))
 }

+ 3 - 3
internal/ui/category_create.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/ui/session"
 	"miniflux.app/v2/internal/ui/view"
 )
@@ -15,7 +15,7 @@ import (
 func (h *handler) showCreateCategoryPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -26,5 +26,5 @@ func (h *handler) showCreateCategoryPage(w http.ResponseWriter, r *http.Request)
 	view.Set("countUnread", h.store.CountUnreadEntries(user.ID))
 	view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(user.ID))
 
-	html.OK(w, r, view.Render("create_category"))
+	response.HTML(w, r, view.Render("create_category"))
 }

+ 5 - 5
internal/ui/category_edit.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/ui/form"
 	"miniflux.app/v2/internal/ui/session"
 	"miniflux.app/v2/internal/ui/view"
@@ -16,18 +16,18 @@ import (
 func (h *handler) showEditCategoryPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	category, err := h.store.Category(request.UserID(r), request.RouteInt64Param(r, "categoryID"))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	if category == nil {
-		html.NotFound(w, r)
+		response.HTMLNotFound(w, r)
 		return
 	}
 
@@ -45,5 +45,5 @@ func (h *handler) showEditCategoryPage(w http.ResponseWriter, r *http.Request) {
 	view.Set("countUnread", h.store.CountUnreadEntries(user.ID))
 	view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(user.ID))
 
-	html.OK(w, r, view.Render("edit_category"))
+	response.HTML(w, r, view.Render("edit_category"))
 }

+ 7 - 7
internal/ui/category_entries.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/ui/session"
@@ -17,19 +17,19 @@ import (
 func (h *handler) showCategoryEntriesPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	categoryID := request.RouteInt64Param(r, "categoryID")
 	category, err := h.store.Category(request.UserID(r), categoryID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	if category == nil {
-		html.NotFound(w, r)
+		response.HTMLNotFound(w, r)
 		return
 	}
 
@@ -44,13 +44,13 @@ func (h *handler) showCategoryEntriesPage(w http.ResponseWriter, r *http.Request
 
 	entries, err := builder.GetEntries()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	count, err := builder.CountEntries()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -67,5 +67,5 @@ func (h *handler) showCategoryEntriesPage(w http.ResponseWriter, r *http.Request
 	view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID))
 	view.Set("showOnlyUnreadEntries", true)
 
-	html.OK(w, r, view.Render("category_entries"))
+	response.HTML(w, r, view.Render("category_entries"))
 }

+ 7 - 7
internal/ui/category_entries_all.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/ui/session"
@@ -17,19 +17,19 @@ import (
 func (h *handler) showCategoryEntriesAllPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	categoryID := request.RouteInt64Param(r, "categoryID")
 	category, err := h.store.Category(request.UserID(r), categoryID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	if category == nil {
-		html.NotFound(w, r)
+		response.HTMLNotFound(w, r)
 		return
 	}
 
@@ -44,13 +44,13 @@ func (h *handler) showCategoryEntriesAllPage(w http.ResponseWriter, r *http.Requ
 
 	entries, err := builder.GetEntries()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	count, err := builder.CountEntries()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -67,5 +67,5 @@ func (h *handler) showCategoryEntriesAllPage(w http.ResponseWriter, r *http.Requ
 	view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID))
 	view.Set("showOnlyUnreadEntries", false)
 
-	html.OK(w, r, view.Render("category_entries"))
+	response.HTML(w, r, view.Render("category_entries"))
 }

+ 7 - 7
internal/ui/category_entries_starred.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/ui/session"
@@ -17,19 +17,19 @@ import (
 func (h *handler) showCategoryEntriesStarredPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	categoryID := request.RouteInt64Param(r, "categoryID")
 	category, err := h.store.Category(request.UserID(r), categoryID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	if category == nil {
-		html.NotFound(w, r)
+		response.HTMLNotFound(w, r)
 		return
 	}
 
@@ -45,13 +45,13 @@ func (h *handler) showCategoryEntriesStarredPage(w http.ResponseWriter, r *http.
 
 	entries, err := builder.GetEntries()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	count, err := builder.CountEntries()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -68,5 +68,5 @@ func (h *handler) showCategoryEntriesStarredPage(w http.ResponseWriter, r *http.
 	view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID))
 	view.Set("showOnlyStarredEntries", true)
 
-	html.OK(w, r, view.Render("category_entries"))
+	response.HTML(w, r, view.Render("category_entries"))
 }

+ 6 - 6
internal/ui/category_feeds.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/ui/session"
 	"miniflux.app/v2/internal/ui/view"
 )
@@ -15,25 +15,25 @@ import (
 func (h *handler) showCategoryFeedsPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	categoryID := request.RouteInt64Param(r, "categoryID")
 	category, err := h.store.Category(request.UserID(r), categoryID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	if category == nil {
-		html.NotFound(w, r)
+		response.HTMLNotFound(w, r)
 		return
 	}
 
 	feeds, err := h.store.FeedsByCategoryWithCounters(user.ID, categoryID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -47,5 +47,5 @@ func (h *handler) showCategoryFeedsPage(w http.ResponseWriter, r *http.Request)
 	view.Set("countUnread", h.store.CountUnreadEntries(user.ID))
 	view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(user.ID))
 
-	html.OK(w, r, view.Render("category_feeds"))
+	response.HTML(w, r, view.Render("category_feeds"))
 }

+ 4 - 4
internal/ui/category_list.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/ui/session"
 	"miniflux.app/v2/internal/ui/view"
 )
@@ -15,13 +15,13 @@ import (
 func (h *handler) showCategoryListPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	categories, err := h.store.CategoriesWithFeedCount(user.ID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -34,5 +34,5 @@ func (h *handler) showCategoryListPage(w http.ResponseWriter, r *http.Request) {
 	view.Set("countUnread", h.store.CountUnreadEntries(user.ID))
 	view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(user.ID))
 
-	html.OK(w, r, view.Render("categories"))
+	response.HTML(w, r, view.Render("categories"))
 }

+ 5 - 5
internal/ui/category_mark_as_read.go

@@ -8,7 +8,7 @@ import (
 	"time"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 )
 
@@ -18,19 +18,19 @@ func (h *handler) markCategoryAsRead(w http.ResponseWriter, r *http.Request) {
 
 	category, err := h.store.Category(userID, categoryID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	if category == nil {
-		html.NotFound(w, r)
+		response.HTMLNotFound(w, r)
 		return
 	}
 
 	if err = h.store.MarkCategoryAsRead(userID, categoryID, time.Now()); err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
-	html.Redirect(w, r, route.Path(h.router, "categories"))
+	response.HTMLRedirect(w, r, route.Path(h.router, "categories"))
 }

+ 4 - 4
internal/ui/category_refresh.go

@@ -10,7 +10,7 @@ import (
 
 	"miniflux.app/v2/internal/config"
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/locale"
 	"miniflux.app/v2/internal/ui/session"
@@ -18,12 +18,12 @@ import (
 
 func (h *handler) refreshCategoryEntriesPage(w http.ResponseWriter, r *http.Request) {
 	categoryID := h.refreshCategory(w, r)
-	html.Redirect(w, r, route.Path(h.router, "categoryEntries", "categoryID", categoryID))
+	response.HTMLRedirect(w, r, route.Path(h.router, "categoryEntries", "categoryID", categoryID))
 }
 
 func (h *handler) refreshCategoryFeedsPage(w http.ResponseWriter, r *http.Request) {
 	categoryID := h.refreshCategory(w, r)
-	html.Redirect(w, r, route.Path(h.router, "categoryFeeds", "categoryID", categoryID))
+	response.HTMLRedirect(w, r, route.Path(h.router, "categoryFeeds", "categoryID", categoryID))
 }
 
 func (h *handler) refreshCategory(w http.ResponseWriter, r *http.Request) int64 {
@@ -47,7 +47,7 @@ func (h *handler) refreshCategory(w http.ResponseWriter, r *http.Request) int64
 
 		jobs, err := batchBuilder.FetchJobs()
 		if err != nil {
-			html.ServerError(w, r, err)
+			response.HTMLServerError(w, r, err)
 			return 0
 		}
 

+ 6 - 6
internal/ui/category_remove.go

@@ -7,33 +7,33 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 )
 
 func (h *handler) removeCategory(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	categoryID := request.RouteInt64Param(r, "categoryID")
 	category, err := h.store.Category(request.UserID(r), categoryID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	if category == nil {
-		html.NotFound(w, r)
+		response.HTMLNotFound(w, r)
 		return
 	}
 
 	if err := h.store.RemoveCategory(user.ID, category.ID); err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
-	html.Redirect(w, r, route.Path(h.router, "categories"))
+	response.HTMLRedirect(w, r, route.Path(h.router, "categories"))
 }

+ 4 - 4
internal/ui/category_remove_feed.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 )
 
@@ -16,14 +16,14 @@ func (h *handler) removeCategoryFeed(w http.ResponseWriter, r *http.Request) {
 	categoryID := request.RouteInt64Param(r, "categoryID")
 
 	if !h.store.CategoryFeedExists(request.UserID(r), categoryID, feedID) {
-		html.NotFound(w, r)
+		response.HTMLNotFound(w, r)
 		return
 	}
 
 	if err := h.store.RemoveFeed(request.UserID(r), feedID); err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
-	html.Redirect(w, r, route.Path(h.router, "categoryFeeds", "categoryID", categoryID))
+	response.HTMLRedirect(w, r, route.Path(h.router, "categoryFeeds", "categoryID", categoryID))
 }

+ 5 - 5
internal/ui/category_save.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/ui/form"
@@ -19,7 +19,7 @@ import (
 func (h *handler) saveCategory(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -37,14 +37,14 @@ func (h *handler) saveCategory(w http.ResponseWriter, r *http.Request) {
 
 	if validationErr := validator.ValidateCategoryCreation(h.store, user.ID, categoryCreationRequest); validationErr != nil {
 		view.Set("errorMessage", validationErr.Translate(user.Language))
-		html.OK(w, r, view.Render("create_category"))
+		response.HTML(w, r, view.Render("create_category"))
 		return
 	}
 
 	if _, err = h.store.CreateCategory(user.ID, categoryCreationRequest); err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
-	html.Redirect(w, r, route.Path(h.router, "categories"))
+	response.HTMLRedirect(w, r, route.Path(h.router, "categories"))
 }

+ 7 - 7
internal/ui/category_update.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/ui/form"
@@ -19,19 +19,19 @@ import (
 func (h *handler) updateCategory(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	categoryID := request.RouteInt64Param(r, "categoryID")
 	category, err := h.store.Category(request.UserID(r), categoryID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	if category == nil {
-		html.NotFound(w, r)
+		response.HTMLNotFound(w, r)
 		return
 	}
 
@@ -53,15 +53,15 @@ func (h *handler) updateCategory(w http.ResponseWriter, r *http.Request) {
 
 	if validationErr := validator.ValidateCategoryModification(h.store, user.ID, category.ID, categoryRequest); validationErr != nil {
 		view.Set("errorMessage", validationErr.Translate(user.Language))
-		html.OK(w, r, view.Render("create_category"))
+		response.HTML(w, r, view.Render("create_category"))
 		return
 	}
 
 	categoryRequest.Patch(category)
 	if err := h.store.UpdateCategory(category); err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
-	html.Redirect(w, r, route.Path(h.router, "categoryFeeds", "categoryID", categoryID))
+	response.HTMLRedirect(w, r, route.Path(h.router, "categoryFeeds", "categoryID", categoryID))
 }

+ 8 - 8
internal/ui/entry_category.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/storage"
@@ -18,7 +18,7 @@ import (
 func (h *handler) showCategoryEntryPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -32,19 +32,19 @@ func (h *handler) showCategoryEntryPage(w http.ResponseWriter, r *http.Request)
 
 	entry, err := builder.GetEntry()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	if entry == nil {
-		html.NotFound(w, r)
+		response.HTMLNotFound(w, r)
 		return
 	}
 
 	if entry.ShouldMarkAsReadOnView(user) {
 		err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead)
 		if err != nil {
-			html.ServerError(w, r, err)
+			response.HTMLServerError(w, r, err)
 			return
 		}
 
@@ -52,7 +52,7 @@ func (h *handler) showCategoryEntryPage(w http.ResponseWriter, r *http.Request)
 	}
 
 	if user.AlwaysOpenExternalLinks {
-		html.Redirect(w, r, entry.URL)
+		response.HTMLRedirect(w, r, entry.URL)
 		return
 	}
 
@@ -60,7 +60,7 @@ func (h *handler) showCategoryEntryPage(w http.ResponseWriter, r *http.Request)
 	entryPaginationBuilder.WithCategoryID(categoryID)
 	prevEntry, nextEntry, err := entryPaginationBuilder.Entries()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -87,5 +87,5 @@ func (h *handler) showCategoryEntryPage(w http.ResponseWriter, r *http.Request)
 	view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(user.ID))
 	view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID))
 
-	html.OK(w, r, view.Render("entry"))
+	response.HTML(w, r, view.Render("entry"))
 }

+ 6 - 6
internal/ui/entry_enclosure_save_position.go

@@ -8,19 +8,19 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/json"
+	"miniflux.app/v2/internal/http/response"
 )
 
 func (h *handler) saveEnclosureProgression(w http.ResponseWriter, r *http.Request) {
 	enclosureID := request.RouteInt64Param(r, "enclosureID")
 	enclosure, err := h.store.GetEnclosure(enclosureID)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	if enclosure == nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
@@ -30,15 +30,15 @@ func (h *handler) saveEnclosureProgression(w http.ResponseWriter, r *http.Reques
 
 	var postData enclosurePositionSaveRequest
 	if err := json_parser.NewDecoder(r.Body).Decode(&postData); err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 	enclosure.MediaProgression = postData.Progression
 
 	if err := h.store.UpdateEnclosure(enclosure); err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
-	json.Created(w, r, map[string]string{"message": "saved"})
+	response.JSONCreated(w, r, map[string]string{"message": "saved"})
 }

+ 8 - 8
internal/ui/entry_feed.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/storage"
@@ -18,7 +18,7 @@ import (
 func (h *handler) showFeedEntryPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -32,19 +32,19 @@ func (h *handler) showFeedEntryPage(w http.ResponseWriter, r *http.Request) {
 
 	entry, err := builder.GetEntry()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	if entry == nil {
-		html.NotFound(w, r)
+		response.HTMLNotFound(w, r)
 		return
 	}
 
 	if entry.ShouldMarkAsReadOnView(user) {
 		err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead)
 		if err != nil {
-			html.ServerError(w, r, err)
+			response.HTMLServerError(w, r, err)
 			return
 		}
 
@@ -52,7 +52,7 @@ func (h *handler) showFeedEntryPage(w http.ResponseWriter, r *http.Request) {
 	}
 
 	if user.AlwaysOpenExternalLinks {
-		html.Redirect(w, r, entry.URL)
+		response.HTMLRedirect(w, r, entry.URL)
 		return
 	}
 
@@ -60,7 +60,7 @@ func (h *handler) showFeedEntryPage(w http.ResponseWriter, r *http.Request) {
 	entryPaginationBuilder.WithFeedID(feedID)
 	prevEntry, nextEntry, err := entryPaginationBuilder.Entries()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -87,5 +87,5 @@ func (h *handler) showFeedEntryPage(w http.ResponseWriter, r *http.Request) {
 	view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(user.ID))
 	view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID))
 
-	html.OK(w, r, view.Render("entry"))
+	response.HTML(w, r, view.Render("entry"))
 }

+ 7 - 7
internal/ui/entry_read.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/storage"
@@ -18,7 +18,7 @@ import (
 func (h *handler) showReadEntryPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -29,17 +29,17 @@ func (h *handler) showReadEntryPage(w http.ResponseWriter, r *http.Request) {
 
 	entry, err := builder.GetEntry()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	if entry == nil {
-		html.NotFound(w, r)
+		response.HTMLNotFound(w, r)
 		return
 	}
 
 	if user.AlwaysOpenExternalLinks {
-		html.Redirect(w, r, entry.URL)
+		response.HTMLRedirect(w, r, entry.URL)
 		return
 	}
 
@@ -47,7 +47,7 @@ func (h *handler) showReadEntryPage(w http.ResponseWriter, r *http.Request) {
 	entryPaginationBuilder.WithStatus(model.EntryStatusRead)
 	prevEntry, nextEntry, err := entryPaginationBuilder.Entries()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -74,5 +74,5 @@ func (h *handler) showReadEntryPage(w http.ResponseWriter, r *http.Request) {
 	view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(user.ID))
 	view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID))
 
-	html.OK(w, r, view.Render("entry"))
+	response.HTML(w, r, view.Render("entry"))
 }

+ 5 - 5
internal/ui/entry_save.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/json"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/integration"
 	"miniflux.app/v2/internal/model"
 )
@@ -20,22 +20,22 @@ func (h *handler) saveEntry(w http.ResponseWriter, r *http.Request) {
 
 	entry, err := builder.GetEntry()
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	if entry == nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
 	userIntegrations, err := h.store.Integration(request.UserID(r))
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	go integration.SendEntry(entry, userIntegrations)
 
-	json.Created(w, r, map[string]string{"message": "saved"})
+	response.JSONCreated(w, r, map[string]string{"message": "saved"})
 }

+ 9 - 9
internal/ui/entry_scraper.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/json"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/locale"
 	"miniflux.app/v2/internal/mediaproxy"
 	"miniflux.app/v2/internal/model"
@@ -25,18 +25,18 @@ func (h *handler) fetchContent(w http.ResponseWriter, r *http.Request) {
 
 	entry, err := entryBuilder.GetEntry()
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	if entry == nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
 	user, err := h.store.UserByID(loggedUserID)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
@@ -44,26 +44,26 @@ func (h *handler) fetchContent(w http.ResponseWriter, r *http.Request) {
 	feedBuilder.WithFeedID(entry.FeedID)
 	feed, err := feedBuilder.GetFeed()
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	if feed == nil {
-		json.NotFound(w, r)
+		response.JSONNotFound(w, r)
 		return
 	}
 
 	if err := processor.ProcessEntryWebPage(feed, entry, user); err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	if err := h.store.UpdateEntryTitleAndContent(entry); err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
 	readingTime := locale.NewPrinter(user.Language).Plural("entry.estimated_reading_time", entry.ReadingTime, entry.ReadingTime)
 
-	json.OK(w, r, map[string]string{"content": mediaproxy.RewriteDocumentWithRelativeProxyURL(h.router, entry.Content), "reading_time": readingTime})
+	response.JSON(w, r, map[string]string{"content": mediaproxy.RewriteDocumentWithRelativeProxyURL(h.router, entry.Content), "reading_time": readingTime})
 }

+ 8 - 8
internal/ui/entry_search.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/storage"
@@ -18,7 +18,7 @@ import (
 func (h *handler) showSearchEntryPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -32,19 +32,19 @@ func (h *handler) showSearchEntryPage(w http.ResponseWriter, r *http.Request) {
 
 	entry, err := builder.GetEntry()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	if entry == nil {
-		html.NotFound(w, r)
+		response.HTMLNotFound(w, r)
 		return
 	}
 
 	if entry.ShouldMarkAsReadOnView(user) {
 		err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead)
 		if err != nil {
-			html.ServerError(w, r, err)
+			response.HTMLServerError(w, r, err)
 			return
 		}
 
@@ -52,7 +52,7 @@ func (h *handler) showSearchEntryPage(w http.ResponseWriter, r *http.Request) {
 	}
 
 	if user.AlwaysOpenExternalLinks {
-		html.Redirect(w, r, entry.URL)
+		response.HTMLRedirect(w, r, entry.URL)
 		return
 	}
 
@@ -68,7 +68,7 @@ func (h *handler) showSearchEntryPage(w http.ResponseWriter, r *http.Request) {
 
 	prevEntry, nextEntry, err := entryPaginationBuilder.Entries()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -97,5 +97,5 @@ func (h *handler) showSearchEntryPage(w http.ResponseWriter, r *http.Request) {
 	view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(user.ID))
 	view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID))
 
-	html.OK(w, r, view.Render("entry"))
+	response.HTML(w, r, view.Render("entry"))
 }

+ 8 - 8
internal/ui/entry_starred.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/storage"
@@ -18,7 +18,7 @@ import (
 func (h *handler) showStarredEntryPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -29,19 +29,19 @@ func (h *handler) showStarredEntryPage(w http.ResponseWriter, r *http.Request) {
 
 	entry, err := builder.GetEntry()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	if entry == nil {
-		html.NotFound(w, r)
+		response.HTMLNotFound(w, r)
 		return
 	}
 
 	if entry.ShouldMarkAsReadOnView(user) {
 		err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead)
 		if err != nil {
-			html.ServerError(w, r, err)
+			response.HTMLServerError(w, r, err)
 			return
 		}
 
@@ -49,7 +49,7 @@ func (h *handler) showStarredEntryPage(w http.ResponseWriter, r *http.Request) {
 	}
 
 	if user.AlwaysOpenExternalLinks {
-		html.Redirect(w, r, entry.URL)
+		response.HTMLRedirect(w, r, entry.URL)
 		return
 	}
 
@@ -57,7 +57,7 @@ func (h *handler) showStarredEntryPage(w http.ResponseWriter, r *http.Request) {
 	entryPaginationBuilder.WithStarred()
 	prevEntry, nextEntry, err := entryPaginationBuilder.Entries()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -84,5 +84,5 @@ func (h *handler) showStarredEntryPage(w http.ResponseWriter, r *http.Request) {
 	view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(user.ID))
 	view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID))
 
-	html.OK(w, r, view.Render("entry"))
+	response.HTML(w, r, view.Render("entry"))
 }

+ 8 - 8
internal/ui/entry_tag.go

@@ -8,7 +8,7 @@ import (
 	"net/url"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/storage"
@@ -19,13 +19,13 @@ import (
 func (h *handler) showTagEntryPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	tagName, err := url.PathUnescape(request.RouteStringParam(r, "tagName"))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 	entryID := request.RouteInt64Param(r, "entryID")
@@ -37,19 +37,19 @@ func (h *handler) showTagEntryPage(w http.ResponseWriter, r *http.Request) {
 
 	entry, err := builder.GetEntry()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	if entry == nil {
-		html.NotFound(w, r)
+		response.HTMLNotFound(w, r)
 		return
 	}
 
 	if entry.ShouldMarkAsReadOnView(user) {
 		err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead)
 		if err != nil {
-			html.ServerError(w, r, err)
+			response.HTMLServerError(w, r, err)
 			return
 		}
 
@@ -60,7 +60,7 @@ func (h *handler) showTagEntryPage(w http.ResponseWriter, r *http.Request) {
 	entryPaginationBuilder.WithTags([]string{tagName})
 	prevEntry, nextEntry, err := entryPaginationBuilder.Entries()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -86,5 +86,5 @@ func (h *handler) showTagEntryPage(w http.ResponseWriter, r *http.Request) {
 	view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(user.ID))
 	view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID))
 
-	html.OK(w, r, view.Render("entry"))
+	response.HTML(w, r, view.Render("entry"))
 }

+ 3 - 3
internal/ui/entry_toggle_starred.go

@@ -7,15 +7,15 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/json"
+	"miniflux.app/v2/internal/http/response"
 )
 
 func (h *handler) toggleStarred(w http.ResponseWriter, r *http.Request) {
 	entryID := request.RouteInt64Param(r, "entryID")
 	if err := h.store.ToggleStarred(request.UserID(r), entryID); err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
-	json.OK(w, r, "OK")
+	response.JSON(w, r, "OK")
 }

+ 9 - 9
internal/ui/entry_unread.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/storage"
@@ -18,7 +18,7 @@ import (
 func (h *handler) showUnreadEntryPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -29,12 +29,12 @@ func (h *handler) showUnreadEntryPage(w http.ResponseWriter, r *http.Request) {
 
 	entry, err := builder.GetEntry()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	if entry == nil {
-		html.Redirect(w, r, route.Path(h.router, "unread"))
+		response.HTMLRedirect(w, r, route.Path(h.router, "unread"))
 		return
 	}
 
@@ -42,7 +42,7 @@ func (h *handler) showUnreadEntryPage(w http.ResponseWriter, r *http.Request) {
 	if entry.Status == model.EntryStatusRead {
 		err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusUnread)
 		if err != nil {
-			html.ServerError(w, r, err)
+			response.HTMLServerError(w, r, err)
 			return
 		}
 	}
@@ -52,7 +52,7 @@ func (h *handler) showUnreadEntryPage(w http.ResponseWriter, r *http.Request) {
 	entryPaginationBuilder.WithGloballyVisible()
 	prevEntry, nextEntry, err := entryPaginationBuilder.Entries()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -74,13 +74,13 @@ func (h *handler) showUnreadEntryPage(w http.ResponseWriter, r *http.Request) {
 	if entry.Status == model.EntryStatusRead {
 		err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead)
 		if err != nil {
-			html.ServerError(w, r, err)
+			response.HTMLServerError(w, r, err)
 			return
 		}
 	}
 
 	if user.AlwaysOpenExternalLinks {
-		html.Redirect(w, r, entry.URL)
+		response.HTMLRedirect(w, r, entry.URL)
 		return
 	}
 
@@ -99,5 +99,5 @@ func (h *handler) showUnreadEntryPage(w http.ResponseWriter, r *http.Request) {
 	// Fetching the counter here avoid to be off by one.
 	view.Set("countUnread", h.store.CountUnreadEntries(user.ID))
 
-	html.OK(w, r, view.Render("entry"))
+	response.HTML(w, r, view.Render("entry"))
 }

+ 5 - 5
internal/ui/entry_update_status.go

@@ -8,7 +8,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/json"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/validator"
 )
@@ -16,20 +16,20 @@ import (
 func (h *handler) updateEntriesStatus(w http.ResponseWriter, r *http.Request) {
 	var entriesStatusUpdateRequest model.EntriesStatusUpdateRequest
 	if err := json_parser.NewDecoder(r.Body).Decode(&entriesStatusUpdateRequest); err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
 	if err := validator.ValidateEntriesStatusUpdateRequest(&entriesStatusUpdateRequest); err != nil {
-		json.BadRequest(w, r, err)
+		response.JSONBadRequest(w, r, err)
 		return
 	}
 
 	count, err := h.store.SetEntriesStatusCount(request.UserID(r), entriesStatusUpdateRequest.EntryIDs, entriesStatusUpdateRequest.Status)
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
-	json.OK(w, r, count)
+	response.JSON(w, r, count)
 }

+ 6 - 6
internal/ui/feed_edit.go

@@ -8,7 +8,7 @@ import (
 
 	"miniflux.app/v2/internal/config"
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/ui/form"
 	"miniflux.app/v2/internal/ui/session"
 	"miniflux.app/v2/internal/ui/view"
@@ -17,25 +17,25 @@ import (
 func (h *handler) showEditFeedPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	feedID := request.RouteInt64Param(r, "feedID")
 	feed, err := h.store.FeedByID(user.ID, feedID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	if feed == nil {
-		html.NotFound(w, r)
+		response.HTMLNotFound(w, r)
 		return
 	}
 
 	categories, err := h.store.Categories(user.ID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -88,5 +88,5 @@ func (h *handler) showEditFeedPage(w http.ResponseWriter, r *http.Request) {
 	view.Set("defaultUserAgent", config.Opts.HTTPClientUserAgent())
 	view.Set("hasProxyConfigured", config.Opts.HasHTTPClientProxyURLConfigured())
 
-	html.OK(w, r, view.Render("edit_feed"))
+	response.HTML(w, r, view.Render("edit_feed"))
 }

+ 7 - 7
internal/ui/feed_entries.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/ui/session"
@@ -17,19 +17,19 @@ import (
 func (h *handler) showFeedEntriesPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	feedID := request.RouteInt64Param(r, "feedID")
 	feed, err := h.store.FeedByID(user.ID, feedID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	if feed == nil {
-		html.NotFound(w, r)
+		response.HTMLNotFound(w, r)
 		return
 	}
 
@@ -44,13 +44,13 @@ func (h *handler) showFeedEntriesPage(w http.ResponseWriter, r *http.Request) {
 
 	entries, err := builder.GetEntries()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	count, err := builder.CountEntries()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -67,5 +67,5 @@ func (h *handler) showFeedEntriesPage(w http.ResponseWriter, r *http.Request) {
 	view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID))
 	view.Set("showOnlyUnreadEntries", true)
 
-	html.OK(w, r, view.Render("feed_entries"))
+	response.HTML(w, r, view.Render("feed_entries"))
 }

+ 7 - 7
internal/ui/feed_entries_all.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/ui/session"
@@ -17,19 +17,19 @@ import (
 func (h *handler) showFeedEntriesAllPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	feedID := request.RouteInt64Param(r, "feedID")
 	feed, err := h.store.FeedByID(user.ID, feedID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	if feed == nil {
-		html.NotFound(w, r)
+		response.HTMLNotFound(w, r)
 		return
 	}
 
@@ -44,13 +44,13 @@ func (h *handler) showFeedEntriesAllPage(w http.ResponseWriter, r *http.Request)
 
 	entries, err := builder.GetEntries()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	count, err := builder.CountEntries()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -67,5 +67,5 @@ func (h *handler) showFeedEntriesAllPage(w http.ResponseWriter, r *http.Request)
 	view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID))
 	view.Set("showOnlyUnreadEntries", false)
 
-	html.OK(w, r, view.Render("feed_entries"))
+	response.HTML(w, r, view.Render("feed_entries"))
 }

+ 2 - 3
internal/ui/feed_icon.go

@@ -9,19 +9,18 @@ import (
 
 	"miniflux.app/v2/internal/http/request"
 	"miniflux.app/v2/internal/http/response"
-	"miniflux.app/v2/internal/http/response/html"
 )
 
 func (h *handler) showFeedIcon(w http.ResponseWriter, r *http.Request) {
 	externalIconID := request.RouteStringParam(r, "externalIconID")
 	icon, err := h.store.IconByExternalID(externalIconID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	if icon == nil {
-		html.NotFound(w, r)
+		response.HTMLNotFound(w, r)
 		return
 	}
 

+ 4 - 4
internal/ui/feed_list.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/ui/session"
 	"miniflux.app/v2/internal/ui/view"
 )
@@ -15,13 +15,13 @@ import (
 func (h *handler) showFeedsPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	feeds, err := h.store.FeedsWithCounters(user.ID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -34,5 +34,5 @@ func (h *handler) showFeedsPage(w http.ResponseWriter, r *http.Request) {
 	view.Set("countUnread", h.store.CountUnreadEntries(user.ID))
 	view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(user.ID))
 
-	html.OK(w, r, view.Render("feeds"))
+	response.HTML(w, r, view.Render("feeds"))
 }

+ 4 - 4
internal/ui/feed_mark_as_read.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 )
 
@@ -17,14 +17,14 @@ func (h *handler) markFeedAsRead(w http.ResponseWriter, r *http.Request) {
 
 	checkedAt, err := h.store.CheckedAt(userID, feedID)
 	if err != nil {
-		html.NotFound(w, r)
+		response.HTMLNotFound(w, r)
 		return
 	}
 
 	if err = h.store.MarkFeedAsRead(userID, feedID, checkedAt); err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
-	html.Redirect(w, r, route.Path(h.router, "feeds"))
+	response.HTMLRedirect(w, r, route.Path(h.router, "feeds"))
 }

+ 4 - 4
internal/ui/feed_refresh.go

@@ -10,7 +10,7 @@ import (
 
 	"miniflux.app/v2/internal/config"
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/locale"
 	feedHandler "miniflux.app/v2/internal/reader/handler"
@@ -29,7 +29,7 @@ func (h *handler) refreshFeed(w http.ResponseWriter, r *http.Request) {
 		)
 	}
 
-	html.Redirect(w, r, route.Path(h.router, "feedEntries", "feedID", feedID))
+	response.HTMLRedirect(w, r, route.Path(h.router, "feedEntries", "feedID", feedID))
 }
 
 func (h *handler) refreshAllFeeds(w http.ResponseWriter, r *http.Request) {
@@ -51,7 +51,7 @@ func (h *handler) refreshAllFeeds(w http.ResponseWriter, r *http.Request) {
 
 		jobs, err := batchBuilder.FetchJobs()
 		if err != nil {
-			html.ServerError(w, r, err)
+			response.HTMLServerError(w, r, err)
 			return
 		}
 
@@ -67,5 +67,5 @@ func (h *handler) refreshAllFeeds(w http.ResponseWriter, r *http.Request) {
 		sess.NewFlashMessage(printer.Print("alert.background_feed_refresh"))
 	}
 
-	html.Redirect(w, r, route.Path(h.router, "feeds"))
+	response.HTMLRedirect(w, r, route.Path(h.router, "feeds"))
 }

+ 4 - 4
internal/ui/feed_remove.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 )
 
@@ -15,14 +15,14 @@ func (h *handler) removeFeed(w http.ResponseWriter, r *http.Request) {
 	feedID := request.RouteInt64Param(r, "feedID")
 
 	if !h.store.FeedExists(request.UserID(r), feedID) {
-		html.NotFound(w, r)
+		response.HTMLNotFound(w, r)
 		return
 	}
 
 	if err := h.store.RemoveFeed(request.UserID(r), feedID); err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
-	html.Redirect(w, r, route.Path(h.router, "feeds"))
+	response.HTMLRedirect(w, r, route.Path(h.router, "feeds"))
 }

+ 8 - 8
internal/ui/feed_update.go

@@ -8,7 +8,7 @@ import (
 
 	"miniflux.app/v2/internal/config"
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/ui/form"
@@ -20,25 +20,25 @@ import (
 func (h *handler) updateFeed(w http.ResponseWriter, r *http.Request) {
 	loggedUser, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	feedID := request.RouteInt64Param(r, "feedID")
 	feed, err := h.store.FeedByID(loggedUser.ID, feedID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	if feed == nil {
-		html.NotFound(w, r)
+		response.HTMLNotFound(w, r)
 		return
 	}
 
 	categories, err := h.store.Categories(loggedUser.ID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -69,15 +69,15 @@ func (h *handler) updateFeed(w http.ResponseWriter, r *http.Request) {
 
 	if validationErr := validator.ValidateFeedModification(h.store, loggedUser.ID, feed.ID, feedModificationRequest); validationErr != nil {
 		view.Set("errorMessage", validationErr.Translate(loggedUser.Language))
-		html.OK(w, r, view.Render("edit_feed"))
+		response.HTML(w, r, view.Render("edit_feed"))
 		return
 	}
 
 	err = h.store.UpdateFeed(feedForm.Merge(feed))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
-	html.Redirect(w, r, route.Path(h.router, "feedEntries", "feedID", feed.ID))
+	response.HTMLRedirect(w, r, route.Path(h.router, "feedEntries", "feedID", feed.ID))
 }

+ 5 - 5
internal/ui/history_entries.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/ui/session"
@@ -17,7 +17,7 @@ import (
 func (h *handler) showHistoryPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -31,13 +31,13 @@ func (h *handler) showHistoryPage(w http.ResponseWriter, r *http.Request) {
 
 	entries, err := builder.GetEntries()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	count, err := builder.CountEntries()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -52,5 +52,5 @@ func (h *handler) showHistoryPage(w http.ResponseWriter, r *http.Request) {
 	view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(user.ID))
 	view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID))
 
-	html.OK(w, r, view.Render("history_entries"))
+	response.HTML(w, r, view.Render("history_entries"))
 }

+ 3 - 3
internal/ui/history_flush.go

@@ -7,15 +7,15 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/json"
+	"miniflux.app/v2/internal/http/response"
 )
 
 func (h *handler) flushHistory(w http.ResponseWriter, r *http.Request) {
 	err := h.store.FlushHistory(request.UserID(r))
 	if err != nil {
-		json.ServerError(w, r, err)
+		response.JSONServerError(w, r, err)
 		return
 	}
 
-	json.OK(w, r, "OK")
+	response.JSON(w, r, "OK")
 }

+ 4 - 4
internal/ui/integration_show.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/ui/form"
 	"miniflux.app/v2/internal/ui/session"
 	"miniflux.app/v2/internal/ui/view"
@@ -16,13 +16,13 @@ import (
 func (h *handler) showIntegrationPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	integration, err := h.store.Integration(user.ID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -156,5 +156,5 @@ func (h *handler) showIntegrationPage(w http.ResponseWriter, r *http.Request) {
 	view.Set("countUnread", h.store.CountUnreadEntries(user.ID))
 	view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(user.ID))
 
-	html.OK(w, r, view.Render("integrations"))
+	response.HTML(w, r, view.Render("integrations"))
 }

+ 8 - 8
internal/ui/integration_update.go

@@ -10,7 +10,7 @@ import (
 
 	"miniflux.app/v2/internal/crypto"
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/locale"
 	"miniflux.app/v2/internal/ui/form"
@@ -24,7 +24,7 @@ func (h *handler) updateIntegration(w http.ResponseWriter, r *http.Request) {
 
 	integration, err := h.store.Integration(userID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -33,7 +33,7 @@ func (h *handler) updateIntegration(w http.ResponseWriter, r *http.Request) {
 
 	if integration.FeverUsername != "" && h.store.HasDuplicateFeverUsername(userID, integration.FeverUsername) {
 		sess.NewFlashErrorMessage(printer.Print("error.duplicate_fever_username"))
-		html.Redirect(w, r, route.Path(h.router, "integrations"))
+		response.HTMLRedirect(w, r, route.Path(h.router, "integrations"))
 		return
 	}
 
@@ -47,7 +47,7 @@ func (h *handler) updateIntegration(w http.ResponseWriter, r *http.Request) {
 
 	if integration.GoogleReaderUsername != "" && h.store.HasDuplicateGoogleReaderUsername(userID, integration.GoogleReaderUsername) {
 		sess.NewFlashErrorMessage(printer.Print("error.duplicate_googlereader_username"))
-		html.Redirect(w, r, route.Path(h.router, "integrations"))
+		response.HTMLRedirect(w, r, route.Path(h.router, "integrations"))
 		return
 	}
 
@@ -55,7 +55,7 @@ func (h *handler) updateIntegration(w http.ResponseWriter, r *http.Request) {
 		if integrationForm.GoogleReaderPassword != "" {
 			integration.GoogleReaderPassword, err = crypto.HashPassword(integrationForm.GoogleReaderPassword)
 			if err != nil {
-				html.ServerError(w, r, err)
+				response.HTMLServerError(w, r, err)
 				return
 			}
 		}
@@ -78,7 +78,7 @@ func (h *handler) updateIntegration(w http.ResponseWriter, r *http.Request) {
 	if integrationForm.LinktacoEnabled {
 		if integrationForm.LinktacoAPIToken == "" || integrationForm.LinktacoOrgSlug == "" {
 			sess.NewFlashErrorMessage(printer.Print("error.linktaco_missing_required_fields"))
-			html.Redirect(w, r, route.Path(h.router, "integrations"))
+			response.HTMLRedirect(w, r, route.Path(h.router, "integrations"))
 			return
 		}
 		if integration.LinktacoVisibility == "" {
@@ -88,10 +88,10 @@ func (h *handler) updateIntegration(w http.ResponseWriter, r *http.Request) {
 
 	err = h.store.UpdateIntegration(integration)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	sess.NewFlashMessage(printer.Print("alert.prefs_saved"))
-	html.Redirect(w, r, route.Path(h.router, "integrations"))
+	response.HTMLRedirect(w, r, route.Path(h.router, "integrations"))
 }

+ 8 - 8
internal/ui/login_check.go

@@ -10,7 +10,7 @@ import (
 	"miniflux.app/v2/internal/config"
 	"miniflux.app/v2/internal/http/cookie"
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/locale"
 	"miniflux.app/v2/internal/ui/form"
@@ -31,7 +31,7 @@ func (h *handler) checkLogin(w http.ResponseWriter, r *http.Request) {
 			slog.String("client_ip", clientIP),
 			slog.String("user_agent", r.UserAgent()),
 		)
-		html.OK(w, r, view.Render("login"))
+		response.HTML(w, r, view.Render("login"))
 		return
 	}
 
@@ -48,7 +48,7 @@ func (h *handler) checkLogin(w http.ResponseWriter, r *http.Request) {
 			slog.String("username", authForm.Username),
 			slog.Any("error", translatedErrorMessage),
 		)
-		html.OK(w, r, view.Render("login"))
+		response.HTML(w, r, view.Render("login"))
 		return
 	}
 
@@ -60,13 +60,13 @@ func (h *handler) checkLogin(w http.ResponseWriter, r *http.Request) {
 			slog.String("username", authForm.Username),
 			slog.Any("error", err),
 		)
-		html.OK(w, r, view.Render("login"))
+		response.HTML(w, r, view.Render("login"))
 		return
 	}
 
 	sessionToken, userID, err := h.store.CreateUserSessionFromUsername(authForm.Username, r.UserAgent(), clientIP)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -82,7 +82,7 @@ func (h *handler) checkLogin(w http.ResponseWriter, r *http.Request) {
 
 	user, err := h.store.UserByID(userID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -97,9 +97,9 @@ func (h *handler) checkLogin(w http.ResponseWriter, r *http.Request) {
 	))
 
 	if redirectURL != "" && urllib.IsRelativePath(redirectURL) {
-		html.Redirect(w, r, redirectURL)
+		response.HTMLRedirect(w, r, redirectURL)
 		return
 	}
 
-	html.Redirect(w, r, route.Path(h.router, user.DefaultHomePage))
+	response.HTMLRedirect(w, r, route.Path(h.router, user.DefaultHomePage))
 }

+ 4 - 4
internal/ui/login_show.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/ui/session"
 	"miniflux.app/v2/internal/ui/view"
@@ -17,11 +17,11 @@ func (h *handler) showLoginPage(w http.ResponseWriter, r *http.Request) {
 	if request.IsAuthenticated(r) {
 		user, err := h.store.UserByID(request.UserID(r))
 		if err != nil {
-			html.ServerError(w, r, err)
+			response.HTMLServerError(w, r, err)
 			return
 		}
 
-		html.Redirect(w, r, route.Path(h.router, user.DefaultHomePage))
+		response.HTMLRedirect(w, r, route.Path(h.router, user.DefaultHomePage))
 		return
 	}
 
@@ -29,5 +29,5 @@ func (h *handler) showLoginPage(w http.ResponseWriter, r *http.Request) {
 	view := view.New(h.tpl, r, sess)
 	redirectURL := request.QueryStringParam(r, "redirect_url", "")
 	view.Set("redirectURL", redirectURL)
-	html.OK(w, r, view.Render("login"))
+	response.HTML(w, r, view.Render("login"))
 }

+ 4 - 4
internal/ui/logout.go

@@ -9,7 +9,7 @@ import (
 	"miniflux.app/v2/internal/config"
 	"miniflux.app/v2/internal/http/cookie"
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/ui/session"
 )
@@ -18,7 +18,7 @@ func (h *handler) logout(w http.ResponseWriter, r *http.Request) {
 	sess := session.New(h.store, request.SessionID(r))
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -26,7 +26,7 @@ func (h *handler) logout(w http.ResponseWriter, r *http.Request) {
 	sess.SetTheme(user.Theme)
 
 	if err := h.store.RemoveUserSessionByToken(user.ID, request.UserSessionToken(r)); err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -36,5 +36,5 @@ func (h *handler) logout(w http.ResponseWriter, r *http.Request) {
 		config.Opts.BasePath(),
 	))
 
-	html.Redirect(w, r, route.Path(h.router, "login"))
+	response.HTMLRedirect(w, r, route.Path(h.router, "login"))
 }

+ 11 - 11
internal/ui/middleware.go

@@ -14,7 +14,7 @@ import (
 	"miniflux.app/v2/internal/crypto"
 	"miniflux.app/v2/internal/http/cookie"
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/storage"
@@ -47,7 +47,7 @@ func (m *middleware) handleUserSession(next http.Handler) http.Handler {
 				values := loginURL.Query()
 				values.Set("redirect_url", r.RequestURI)
 				loginURL.RawQuery = values.Encode()
-				html.Redirect(w, r, loginURL.String())
+				response.HTMLRedirect(w, r, loginURL.String())
 			}
 		} else {
 			slog.Debug("User session found",
@@ -86,14 +86,14 @@ func (m *middleware) handleAppSession(next http.Handler) http.Handler {
 				)
 				session, err = m.store.CreateAppSessionWithUserPrefs(userID)
 				if err != nil {
-					html.ServerError(w, r, err)
+					response.HTMLServerError(w, r, err)
 					return
 				}
 			} else {
 				slog.Debug("App session not found, creating a new one")
 				session, err = m.store.CreateAppSession()
 				if err != nil {
-					html.ServerError(w, r, err)
+					response.HTMLServerError(w, r, err)
 					return
 				}
 			}
@@ -113,11 +113,11 @@ func (m *middleware) handleAppSession(next http.Handler) http.Handler {
 				)
 
 				if mux.CurrentRoute(r).GetName() == "checkLogin" {
-					html.Redirect(w, r, route.Path(m.router, "login"))
+					response.HTMLRedirect(w, r, route.Path(m.router, "login"))
 					return
 				}
 
-				html.BadRequest(w, r, errors.New("invalid or missing CSRF"))
+				response.HTMLBadRequest(w, r, errors.New("invalid or missing CSRF"))
 				return
 			}
 		}
@@ -235,7 +235,7 @@ func (m *middleware) handleAuthProxy(next http.Handler) http.Handler {
 
 		user, err := m.store.UserByUsername(username)
 		if err != nil {
-			html.ServerError(w, r, err)
+			response.HTMLServerError(w, r, err)
 			return
 		}
 
@@ -248,19 +248,19 @@ func (m *middleware) handleAuthProxy(next http.Handler) http.Handler {
 					slog.String("user_agent", r.UserAgent()),
 					slog.String("username", username),
 				)
-				html.Forbidden(w, r)
+				response.HTMLForbidden(w, r)
 				return
 			}
 
 			if user, err = m.store.CreateUser(&model.UserCreationRequest{Username: username}); err != nil {
-				html.ServerError(w, r, err)
+				response.HTMLServerError(w, r, err)
 				return
 			}
 		}
 
 		sessionToken, _, err := m.store.CreateUserSessionFromUsername(user.Username, r.UserAgent(), clientIP)
 		if err != nil {
-			html.ServerError(w, r, err)
+			response.HTMLServerError(w, r, err)
 			return
 		}
 
@@ -286,6 +286,6 @@ func (m *middleware) handleAuthProxy(next http.Handler) http.Handler {
 			config.Opts.BasePath(),
 		))
 
-		html.Redirect(w, r, route.Path(m.router, user.DefaultHomePage))
+		response.HTMLRedirect(w, r, route.Path(m.router, user.DefaultHomePage))
 	})
 }

+ 16 - 16
internal/ui/oauth2_callback.go

@@ -12,7 +12,7 @@ import (
 	"miniflux.app/v2/internal/config"
 	"miniflux.app/v2/internal/http/cookie"
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/locale"
 	"miniflux.app/v2/internal/model"
@@ -23,14 +23,14 @@ func (h *handler) oauth2Callback(w http.ResponseWriter, r *http.Request) {
 	provider := request.RouteStringParam(r, "provider")
 	if provider == "" {
 		slog.Warn("Invalid or missing OAuth2 provider")
-		html.Redirect(w, r, route.Path(h.router, "login"))
+		response.HTMLRedirect(w, r, route.Path(h.router, "login"))
 		return
 	}
 
 	code := request.QueryStringParam(r, "code", "")
 	if code == "" {
 		slog.Warn("No code received on OAuth2 callback")
-		html.Redirect(w, r, route.Path(h.router, "login"))
+		response.HTMLRedirect(w, r, route.Path(h.router, "login"))
 		return
 	}
 
@@ -40,7 +40,7 @@ func (h *handler) oauth2Callback(w http.ResponseWriter, r *http.Request) {
 			slog.String("expected", request.OAuth2State(r)),
 			slog.String("received", state),
 		)
-		html.Redirect(w, r, route.Path(h.router, "login"))
+		response.HTMLRedirect(w, r, route.Path(h.router, "login"))
 		return
 	}
 
@@ -50,7 +50,7 @@ func (h *handler) oauth2Callback(w http.ResponseWriter, r *http.Request) {
 			slog.String("provider", provider),
 			slog.Any("error", err),
 		)
-		html.Redirect(w, r, route.Path(h.router, "login"))
+		response.HTMLRedirect(w, r, route.Path(h.router, "login"))
 		return
 	}
 
@@ -60,7 +60,7 @@ func (h *handler) oauth2Callback(w http.ResponseWriter, r *http.Request) {
 			slog.String("provider", provider),
 			slog.Any("error", err),
 		)
-		html.Redirect(w, r, route.Path(h.router, "login"))
+		response.HTMLRedirect(w, r, route.Path(h.router, "login"))
 		return
 	}
 
@@ -70,7 +70,7 @@ func (h *handler) oauth2Callback(w http.ResponseWriter, r *http.Request) {
 	if request.IsAuthenticated(r) {
 		loggedUser, err := h.store.UserByID(request.UserID(r))
 		if err != nil {
-			html.ServerError(w, r, err)
+			response.HTMLServerError(w, r, err)
 			return
 		}
 
@@ -81,35 +81,35 @@ func (h *handler) oauth2Callback(w http.ResponseWriter, r *http.Request) {
 				slog.String("oauth2_profile_id", profile.ID),
 			)
 			sess.NewFlashErrorMessage(printer.Print("error.duplicate_linked_account"))
-			html.Redirect(w, r, route.Path(h.router, "settings"))
+			response.HTMLRedirect(w, r, route.Path(h.router, "settings"))
 			return
 		}
 
 		authProvider.PopulateUserWithProfileID(loggedUser, profile)
 		if err := h.store.UpdateUser(loggedUser); err != nil {
-			html.ServerError(w, r, err)
+			response.HTMLServerError(w, r, err)
 			return
 		}
 
 		sess.NewFlashMessage(printer.Print("alert.account_linked"))
-		html.Redirect(w, r, route.Path(h.router, "settings"))
+		response.HTMLRedirect(w, r, route.Path(h.router, "settings"))
 		return
 	}
 
 	user, err := h.store.UserByField(profile.Key, profile.ID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	if user == nil {
 		if !config.Opts.IsOAuth2UserCreationAllowed() {
-			html.Forbidden(w, r)
+			response.HTMLForbidden(w, r)
 			return
 		}
 
 		if h.store.UserExists(profile.Username) {
-			html.BadRequest(w, r, errors.New(printer.Print("error.user_already_exists")))
+			response.HTMLBadRequest(w, r, errors.New(printer.Print("error.user_already_exists")))
 			return
 		}
 
@@ -118,7 +118,7 @@ func (h *handler) oauth2Callback(w http.ResponseWriter, r *http.Request) {
 
 		user, err = h.store.CreateUser(userCreationRequest)
 		if err != nil {
-			html.ServerError(w, r, err)
+			response.HTMLServerError(w, r, err)
 			return
 		}
 	}
@@ -126,7 +126,7 @@ func (h *handler) oauth2Callback(w http.ResponseWriter, r *http.Request) {
 	clientIP := request.ClientIP(r)
 	sessionToken, _, err := h.store.CreateUserSessionFromUsername(user.Username, r.UserAgent(), clientIP)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -149,5 +149,5 @@ func (h *handler) oauth2Callback(w http.ResponseWriter, r *http.Request) {
 		config.Opts.BasePath(),
 	))
 
-	html.Redirect(w, r, route.Path(h.router, user.DefaultHomePage))
+	response.HTMLRedirect(w, r, route.Path(h.router, user.DefaultHomePage))
 }

+ 4 - 4
internal/ui/oauth2_redirect.go

@@ -8,7 +8,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/oauth2"
 	"miniflux.app/v2/internal/ui/session"
@@ -18,7 +18,7 @@ func (h *handler) oauth2Redirect(w http.ResponseWriter, r *http.Request) {
 	provider := request.RouteStringParam(r, "provider")
 	if provider == "" {
 		slog.Warn("Invalid or missing OAuth2 provider")
-		html.Redirect(w, r, route.Path(h.router, "login"))
+		response.HTMLRedirect(w, r, route.Path(h.router, "login"))
 		return
 	}
 
@@ -28,7 +28,7 @@ func (h *handler) oauth2Redirect(w http.ResponseWriter, r *http.Request) {
 			slog.String("provider", provider),
 			slog.Any("error", err),
 		)
-		html.Redirect(w, r, route.Path(h.router, "login"))
+		response.HTMLRedirect(w, r, route.Path(h.router, "login"))
 		return
 	}
 
@@ -38,5 +38,5 @@ func (h *handler) oauth2Redirect(w http.ResponseWriter, r *http.Request) {
 	sess.SetOAuth2State(auth.State())
 	sess.SetOAuth2CodeVerifier(auth.CodeVerifier())
 
-	html.Redirect(w, r, auth.RedirectURL())
+	response.HTMLRedirect(w, r, auth.RedirectURL())
 }

+ 9 - 9
internal/ui/oauth2_unlink.go

@@ -9,7 +9,7 @@ import (
 
 	"miniflux.app/v2/internal/config"
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/locale"
 	"miniflux.app/v2/internal/ui/session"
@@ -20,14 +20,14 @@ func (h *handler) oauth2Unlink(w http.ResponseWriter, r *http.Request) {
 		slog.Warn("blocking oauth2 unlink attempt, local auth is disabled",
 			slog.String("user_agent", r.UserAgent()),
 		)
-		html.Redirect(w, r, route.Path(h.router, "login"))
+		response.HTMLRedirect(w, r, route.Path(h.router, "login"))
 		return
 	}
 
 	provider := request.RouteStringParam(r, "provider")
 	if provider == "" {
 		slog.Warn("Invalid or missing OAuth2 provider")
-		html.Redirect(w, r, route.Path(h.router, "login"))
+		response.HTMLRedirect(w, r, route.Path(h.router, "login"))
 		return
 	}
 
@@ -37,19 +37,19 @@ func (h *handler) oauth2Unlink(w http.ResponseWriter, r *http.Request) {
 			slog.String("provider", provider),
 			slog.Any("error", err),
 		)
-		html.Redirect(w, r, route.Path(h.router, "settings"))
+		response.HTMLRedirect(w, r, route.Path(h.router, "settings"))
 		return
 	}
 
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	hasPassword, err := h.store.HasPassword(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -57,16 +57,16 @@ func (h *handler) oauth2Unlink(w http.ResponseWriter, r *http.Request) {
 	printer := locale.NewPrinter(request.UserLanguage(r))
 	if !hasPassword {
 		sess.NewFlashErrorMessage(printer.Print("error.unlink_account_without_password"))
-		html.Redirect(w, r, route.Path(h.router, "settings"))
+		response.HTMLRedirect(w, r, route.Path(h.router, "settings"))
 		return
 	}
 
 	authProvider.UnsetUserProfileID(user)
 	if err := h.store.UpdateUser(user); err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	sess.NewFlashMessage(printer.Print("alert.account_unlinked"))
-	html.Redirect(w, r, route.Path(h.router, "settings"))
+	response.HTMLRedirect(w, r, route.Path(h.router, "settings"))
 }

+ 2 - 2
internal/ui/offline.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/ui/session"
 	"miniflux.app/v2/internal/ui/view"
 )
@@ -15,5 +15,5 @@ import (
 func (h *handler) showOfflinePage(w http.ResponseWriter, r *http.Request) {
 	sess := session.New(h.store, request.SessionID(r))
 	view := view.New(h.tpl, r, sess)
-	html.OK(w, r, view.Render("offline"))
+	response.HTML(w, r, view.Render("offline"))
 }

+ 4 - 4
internal/ui/opml_export.go

@@ -7,17 +7,17 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
-	"miniflux.app/v2/internal/http/response/xml"
+	"miniflux.app/v2/internal/http/response"
+
 	"miniflux.app/v2/internal/reader/opml"
 )
 
 func (h *handler) exportFeeds(w http.ResponseWriter, r *http.Request) {
 	opmlExport, err := opml.NewHandler(h.store).Export(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
-	xml.Attachment(w, r, "feeds.opml", opmlExport)
+	response.XMLAttachment(w, r, "feeds.opml", opmlExport)
 }

+ 3 - 3
internal/ui/opml_import.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/ui/session"
 	"miniflux.app/v2/internal/ui/view"
 )
@@ -15,7 +15,7 @@ import (
 func (h *handler) showImportPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -26,5 +26,5 @@ func (h *handler) showImportPage(w http.ResponseWriter, r *http.Request) {
 	view.Set("countUnread", h.store.CountUnreadEntries(user.ID))
 	view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(user.ID))
 
-	html.OK(w, r, view.Render("import"))
+	response.HTML(w, r, view.Render("import"))
 }

+ 11 - 11
internal/ui/opml_upload.go

@@ -10,7 +10,7 @@ import (
 
 	"miniflux.app/v2/internal/config"
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/locale"
 	"miniflux.app/v2/internal/proxyrotator"
@@ -23,7 +23,7 @@ import (
 func (h *handler) uploadOPML(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -34,7 +34,7 @@ func (h *handler) uploadOPML(w http.ResponseWriter, r *http.Request) {
 			slog.Any("error", err),
 		)
 
-		html.Redirect(w, r, route.Path(h.router, "import"))
+		response.HTMLRedirect(w, r, route.Path(h.router, "import"))
 		return
 	}
 	defer file.Close()
@@ -54,29 +54,29 @@ func (h *handler) uploadOPML(w http.ResponseWriter, r *http.Request) {
 
 	if fileHeader.Size == 0 {
 		view.Set("errorMessage", locale.NewLocalizedError("error.empty_file").Translate(user.Language))
-		html.OK(w, r, view.Render("import"))
+		response.HTML(w, r, view.Render("import"))
 		return
 	}
 
 	if impErr := opml.NewHandler(h.store).Import(user.ID, file); impErr != nil {
 		view.Set("errorMessage", impErr)
-		html.OK(w, r, view.Render("import"))
+		response.HTML(w, r, view.Render("import"))
 		return
 	}
 
-	html.Redirect(w, r, route.Path(h.router, "feeds"))
+	response.HTMLRedirect(w, r, route.Path(h.router, "feeds"))
 }
 
 func (h *handler) fetchOPML(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	opmlFileURL := strings.TrimSpace(r.FormValue("url"))
 	if opmlFileURL == "" {
-		html.Redirect(w, r, route.Path(h.router, "import"))
+		response.HTMLRedirect(w, r, route.Path(h.router, "import"))
 		return
 	}
 
@@ -102,15 +102,15 @@ func (h *handler) fetchOPML(w http.ResponseWriter, r *http.Request) {
 	if localizedError := responseHandler.LocalizedError(); localizedError != nil {
 		slog.Warn("Unable to fetch OPML file", slog.String("opml_file_url", opmlFileURL), slog.Any("error", localizedError.Error()))
 		view.Set("errorMessage", localizedError.Translate(user.Language))
-		html.OK(w, r, view.Render("import"))
+		response.HTML(w, r, view.Render("import"))
 		return
 	}
 
 	if impErr := opml.NewHandler(h.store).Import(user.ID, responseHandler.Body(config.Opts.HTTPClientMaxBodySize())); impErr != nil {
 		view.Set("errorMessage", impErr)
-		html.OK(w, r, view.Render("import"))
+		response.HTML(w, r, view.Render("import"))
 		return
 	}
 
-	html.Redirect(w, r, route.Path(h.router, "feeds"))
+	response.HTMLRedirect(w, r, route.Path(h.router, "feeds"))
 }

+ 11 - 11
internal/ui/proxy.go

@@ -19,7 +19,7 @@ import (
 	"miniflux.app/v2/internal/crypto"
 	"miniflux.app/v2/internal/http/request"
 	"miniflux.app/v2/internal/http/response"
-	"miniflux.app/v2/internal/http/response/html"
+
 	"miniflux.app/v2/internal/reader/fetcher"
 	"miniflux.app/v2/internal/reader/rewrite"
 )
@@ -33,20 +33,20 @@ func (h *handler) mediaProxy(w http.ResponseWriter, r *http.Request) {
 
 	encodedURL := request.RouteStringParam(r, "encodedURL")
 	if encodedURL == "" {
-		html.BadRequest(w, r, errors.New("no URL provided"))
+		response.HTMLBadRequest(w, r, errors.New("no URL provided"))
 		return
 	}
 
 	encodedDigest := request.RouteStringParam(r, "encodedDigest")
 	decodedDigest, err := base64.URLEncoding.DecodeString(encodedDigest)
 	if err != nil {
-		html.BadRequest(w, r, errors.New("unable to decode this digest"))
+		response.HTMLBadRequest(w, r, errors.New("unable to decode this digest"))
 		return
 	}
 
 	decodedURL, err := base64.URLEncoding.DecodeString(encodedURL)
 	if err != nil {
-		html.BadRequest(w, r, errors.New("unable to decode this URL"))
+		response.HTMLBadRequest(w, r, errors.New("unable to decode this URL"))
 		return
 	}
 
@@ -55,28 +55,28 @@ func (h *handler) mediaProxy(w http.ResponseWriter, r *http.Request) {
 	expectedMAC := mac.Sum(nil)
 
 	if !hmac.Equal(decodedDigest, expectedMAC) {
-		html.Forbidden(w, r)
+		response.HTMLForbidden(w, r)
 		return
 	}
 
 	parsedMediaURL, err := url.Parse(string(decodedURL))
 	if err != nil {
-		html.BadRequest(w, r, errors.New("invalid URL provided"))
+		response.HTMLBadRequest(w, r, errors.New("invalid URL provided"))
 		return
 	}
 
 	if parsedMediaURL.Scheme != "http" && parsedMediaURL.Scheme != "https" {
-		html.BadRequest(w, r, errors.New("invalid URL provided"))
+		response.HTMLBadRequest(w, r, errors.New("invalid URL provided"))
 		return
 	}
 
 	if parsedMediaURL.Host == "" {
-		html.BadRequest(w, r, errors.New("invalid URL provided"))
+		response.HTMLBadRequest(w, r, errors.New("invalid URL provided"))
 		return
 	}
 
 	if !parsedMediaURL.IsAbs() {
-		html.BadRequest(w, r, errors.New("invalid URL provided"))
+		response.HTMLBadRequest(w, r, errors.New("invalid URL provided"))
 		return
 	}
 
@@ -110,7 +110,7 @@ func (h *handler) mediaProxy(w http.ResponseWriter, r *http.Request) {
 				slog.String("media_url", mediaURL),
 				slog.Any("error", err),
 			)
-			html.Forbidden(w, r)
+			response.HTMLForbidden(w, r)
 			return
 		}
 
@@ -128,7 +128,7 @@ func (h *handler) mediaProxy(w http.ResponseWriter, r *http.Request) {
 			slog.String("media_url", mediaURL),
 			slog.Int("status_code", resp.StatusCode),
 		)
-		html.RequestedRangeNotSatisfiable(w, r, resp.Header.Get("Content-Range"))
+		response.HTMLRequestedRangeNotSatisfiable(w, r, resp.Header.Get("Content-Range"))
 		return
 	}
 	if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusPartialContent {

+ 5 - 5
internal/ui/search.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/ui/session"
@@ -17,7 +17,7 @@ import (
 func (h *handler) showSearchPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -40,13 +40,13 @@ func (h *handler) showSearchPage(w http.ResponseWriter, r *http.Request) {
 
 		entries, err = builder.GetEntries()
 		if err != nil {
-			html.ServerError(w, r, err)
+			response.HTMLServerError(w, r, err)
 			return
 		}
 
 		entriesCount, err = builder.CountEntries()
 		if err != nil {
-			html.ServerError(w, r, err)
+			response.HTMLServerError(w, r, err)
 			return
 		}
 	}
@@ -68,5 +68,5 @@ func (h *handler) showSearchPage(w http.ResponseWriter, r *http.Request) {
 	view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(user.ID))
 	view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID))
 
-	html.OK(w, r, view.Render("search"))
+	response.HTML(w, r, view.Render("search"))
 }

+ 4 - 4
internal/ui/session_list.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/ui/session"
 	"miniflux.app/v2/internal/ui/view"
 )
@@ -15,13 +15,13 @@ import (
 func (h *handler) showSessionsPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	sessions, err := h.store.UserSessions(user.ID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -38,5 +38,5 @@ func (h *handler) showSessionsPage(w http.ResponseWriter, r *http.Request) {
 	view.Set("countUnread", h.store.CountUnreadEntries(user.ID))
 	view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(user.ID))
 
-	html.OK(w, r, view.Render("sessions"))
+	response.HTML(w, r, view.Render("sessions"))
 }

+ 3 - 3
internal/ui/session_remove.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 )
 
@@ -15,9 +15,9 @@ func (h *handler) removeSession(w http.ResponseWriter, r *http.Request) {
 	sessionID := request.RouteInt64Param(r, "sessionID")
 	err := h.store.RemoveUserSessionByID(request.UserID(r), sessionID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
-	html.Redirect(w, r, route.Path(h.router, "sessions"))
+	response.HTMLRedirect(w, r, route.Path(h.router, "sessions"))
 }

+ 4 - 4
internal/ui/settings_show.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/locale"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/timezone"
@@ -19,7 +19,7 @@ import (
 func (h *handler) showSettingsPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -53,7 +53,7 @@ func (h *handler) showSettingsPage(w http.ResponseWriter, r *http.Request) {
 
 	creds, err := h.store.WebAuthnCredentialsByUserID(user.ID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -78,5 +78,5 @@ func (h *handler) showSettingsPage(w http.ResponseWriter, r *http.Request) {
 	view.Set("countWebAuthnCerts", h.store.CountWebAuthnCredentialsByUserID(user.ID))
 	view.Set("webAuthnCerts", creds)
 
-	html.OK(w, r, view.Render("settings"))
+	response.HTML(w, r, view.Render("settings"))
 }

+ 7 - 7
internal/ui/settings_update.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/locale"
 	"miniflux.app/v2/internal/model"
@@ -21,13 +21,13 @@ import (
 func (h *handler) updateSettings(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	creds, err := h.store.WebAuthnCredentialsByUserID(user.ID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -56,7 +56,7 @@ func (h *handler) updateSettings(w http.ResponseWriter, r *http.Request) {
 
 	if validationErr := settingsForm.Validate(); validationErr != nil {
 		view.Set("errorMessage", validationErr.Translate(user.Language))
-		html.OK(w, r, view.Render("settings"))
+		response.HTML(w, r, view.Render("settings"))
 		return
 	}
 
@@ -83,18 +83,18 @@ func (h *handler) updateSettings(w http.ResponseWriter, r *http.Request) {
 
 	if validationErr := validator.ValidateUserModification(h.store, user.ID, userModificationRequest); validationErr != nil {
 		view.Set("errorMessage", validationErr.Translate(user.Language))
-		html.OK(w, r, view.Render("settings"))
+		response.HTML(w, r, view.Render("settings"))
 		return
 	}
 
 	err = h.store.UpdateUser(settingsForm.Merge(user))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	sess.SetLanguage(user.Language)
 	sess.SetTheme(user.Theme)
 	sess.NewFlashMessage(locale.NewPrinter(request.UserLanguage(r)).Printf("alert.prefs_saved"))
-	html.Redirect(w, r, route.Path(h.router, "settings"))
+	response.HTMLRedirect(w, r, route.Path(h.router, "settings"))
 }

+ 7 - 7
internal/ui/share.go

@@ -9,7 +9,7 @@ import (
 
 	"miniflux.app/v2/internal/http/request"
 	"miniflux.app/v2/internal/http/response"
-	"miniflux.app/v2/internal/http/response/html"
+
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/storage"
 	"miniflux.app/v2/internal/ui/session"
@@ -20,27 +20,27 @@ func (h *handler) createSharedEntry(w http.ResponseWriter, r *http.Request) {
 	entryID := request.RouteInt64Param(r, "entryID")
 	shareCode, err := h.store.EntryShareCode(request.UserID(r), entryID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
-	html.Redirect(w, r, route.Path(h.router, "sharedEntry", "shareCode", shareCode))
+	response.HTMLRedirect(w, r, route.Path(h.router, "sharedEntry", "shareCode", shareCode))
 }
 
 func (h *handler) unshareEntry(w http.ResponseWriter, r *http.Request) {
 	entryID := request.RouteInt64Param(r, "entryID")
 	if err := h.store.UnshareEntry(request.UserID(r), entryID); err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
-	html.Redirect(w, r, route.Path(h.router, "sharedEntries"))
+	response.HTMLRedirect(w, r, route.Path(h.router, "sharedEntries"))
 }
 
 func (h *handler) sharedEntry(w http.ResponseWriter, r *http.Request) {
 	shareCode := request.RouteStringParam(r, "shareCode")
 	if shareCode == "" {
-		html.NotFound(w, r)
+		response.HTMLNotFound(w, r)
 		return
 	}
 
@@ -51,7 +51,7 @@ func (h *handler) sharedEntry(w http.ResponseWriter, r *http.Request) {
 
 		entry, err := builder.GetEntry()
 		if err != nil || entry == nil {
-			html.NotFound(w, r)
+			response.HTMLNotFound(w, r)
 			return
 		}
 

+ 5 - 5
internal/ui/shared_entries.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/ui/session"
 	"miniflux.app/v2/internal/ui/view"
@@ -16,7 +16,7 @@ import (
 func (h *handler) sharedEntries(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -30,13 +30,13 @@ func (h *handler) sharedEntries(w http.ResponseWriter, r *http.Request) {
 
 	entries, err := builder.GetEntries()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	count, err := builder.CountEntries()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -51,5 +51,5 @@ func (h *handler) sharedEntries(w http.ResponseWriter, r *http.Request) {
 	view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(user.ID))
 	view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID))
 
-	html.OK(w, r, view.Render("shared_entries"))
+	response.HTML(w, r, view.Render("shared_entries"))
 }

+ 5 - 5
internal/ui/starred_entries.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/ui/session"
@@ -17,7 +17,7 @@ import (
 func (h *handler) showStarredPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -32,13 +32,13 @@ func (h *handler) showStarredPage(w http.ResponseWriter, r *http.Request) {
 
 	entries, err := builder.GetEntries()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	count, err := builder.CountEntries()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -53,5 +53,5 @@ func (h *handler) showStarredPage(w http.ResponseWriter, r *http.Request) {
 	view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(user.ID))
 	view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID))
 
-	html.OK(w, r, view.Render("starred_entries"))
+	response.HTML(w, r, view.Render("starred_entries"))
 }

+ 8 - 8
internal/ui/starred_entry_category.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/storage"
@@ -18,7 +18,7 @@ import (
 func (h *handler) showStarredCategoryEntryPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -32,19 +32,19 @@ func (h *handler) showStarredCategoryEntryPage(w http.ResponseWriter, r *http.Re
 
 	entry, err := builder.GetEntry()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	if entry == nil {
-		html.NotFound(w, r)
+		response.HTMLNotFound(w, r)
 		return
 	}
 
 	if entry.ShouldMarkAsReadOnView(user) {
 		err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead)
 		if err != nil {
-			html.ServerError(w, r, err)
+			response.HTMLServerError(w, r, err)
 			return
 		}
 
@@ -52,7 +52,7 @@ func (h *handler) showStarredCategoryEntryPage(w http.ResponseWriter, r *http.Re
 	}
 
 	if user.AlwaysOpenExternalLinks {
-		html.Redirect(w, r, entry.URL)
+		response.HTMLRedirect(w, r, entry.URL)
 		return
 	}
 
@@ -62,7 +62,7 @@ func (h *handler) showStarredCategoryEntryPage(w http.ResponseWriter, r *http.Re
 
 	prevEntry, nextEntry, err := entryPaginationBuilder.Entries()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -89,5 +89,5 @@ func (h *handler) showStarredCategoryEntryPage(w http.ResponseWriter, r *http.Re
 	view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(user.ID))
 	view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID))
 
-	html.OK(w, r, view.Render("entry"))
+	response.HTML(w, r, view.Render("entry"))
 }

+ 2 - 2
internal/ui/static_app_icon.go

@@ -10,7 +10,7 @@ import (
 
 	"miniflux.app/v2/internal/http/request"
 	"miniflux.app/v2/internal/http/response"
-	"miniflux.app/v2/internal/http/response/html"
+
 	"miniflux.app/v2/internal/ui/static"
 )
 
@@ -18,7 +18,7 @@ func (h *handler) showAppIcon(w http.ResponseWriter, r *http.Request) {
 	filename := request.RouteStringParam(r, "filename")
 	value, ok := static.BinaryBundles[filename]
 	if !ok {
-		html.NotFound(w, r)
+		response.HTMLNotFound(w, r)
 		return
 	}
 

+ 2 - 2
internal/ui/static_favicon.go

@@ -8,14 +8,14 @@ import (
 	"time"
 
 	"miniflux.app/v2/internal/http/response"
-	"miniflux.app/v2/internal/http/response/html"
+
 	"miniflux.app/v2/internal/ui/static"
 )
 
 func (h *handler) showFavicon(w http.ResponseWriter, r *http.Request) {
 	value, ok := static.BinaryBundles["favicon.ico"]
 	if !ok {
-		html.NotFound(w, r)
+		response.HTMLNotFound(w, r)
 		return
 	}
 

+ 2 - 2
internal/ui/static_javascript.go

@@ -11,7 +11,7 @@ import (
 
 	"miniflux.app/v2/internal/http/request"
 	"miniflux.app/v2/internal/http/response"
-	"miniflux.app/v2/internal/http/response/html"
+
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/ui/static"
 )
@@ -23,7 +23,7 @@ func (h *handler) showJavascript(w http.ResponseWriter, r *http.Request) {
 	filename := request.RouteStringParam(r, "name")
 	js, found := static.JavascriptBundles[filename]
 	if !found {
-		html.NotFound(w, r)
+		response.HTMLNotFound(w, r)
 		return
 	}
 

+ 3 - 3
internal/ui/static_manifest.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/json"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/locale"
 	"miniflux.app/v2/internal/model"
@@ -63,7 +63,7 @@ func (h *handler) showWebManifest(w http.ResponseWriter, r *http.Request) {
 	if request.IsAuthenticated(r) {
 		user, err := h.store.UserByID(request.UserID(r))
 		if err != nil {
-			json.ServerError(w, r, err)
+			response.JSONServerError(w, r, err)
 			return
 		}
 		displayMode = user.DisplayMode
@@ -111,5 +111,5 @@ func (h *handler) showWebManifest(w http.ResponseWriter, r *http.Request) {
 		},
 	}
 
-	json.OK(w, r, manifest)
+	response.JSON(w, r, manifest)
 }

+ 2 - 2
internal/ui/static_stylesheet.go

@@ -9,7 +9,7 @@ import (
 
 	"miniflux.app/v2/internal/http/request"
 	"miniflux.app/v2/internal/http/response"
-	"miniflux.app/v2/internal/http/response/html"
+
 	"miniflux.app/v2/internal/ui/static"
 )
 
@@ -17,7 +17,7 @@ func (h *handler) showStylesheet(w http.ResponseWriter, r *http.Request) {
 	filename := request.RouteStringParam(r, "name")
 	m, found := static.StylesheetBundles[filename]
 	if !found {
-		html.NotFound(w, r)
+		response.HTMLNotFound(w, r)
 		return
 	}
 

+ 4 - 4
internal/ui/subscription_add.go

@@ -8,7 +8,7 @@ import (
 
 	"miniflux.app/v2/internal/config"
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/ui/form"
 	"miniflux.app/v2/internal/ui/session"
 	"miniflux.app/v2/internal/ui/view"
@@ -17,13 +17,13 @@ import (
 func (h *handler) showAddSubscriptionPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	categories, err := h.store.Categories(user.ID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -38,5 +38,5 @@ func (h *handler) showAddSubscriptionPage(w http.ResponseWriter, r *http.Request
 	view.Set("form", &form.SubscriptionForm{CategoryID: 0})
 	view.Set("hasProxyConfigured", config.Opts.HasHTTPClientProxyURLConfigured())
 
-	html.OK(w, r, view.Render("add_subscription"))
+	response.HTML(w, r, view.Render("add_subscription"))
 }

+ 4 - 4
internal/ui/subscription_bookmarklet.go

@@ -9,7 +9,7 @@ import (
 
 	"miniflux.app/v2/internal/config"
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/ui/form"
 	"miniflux.app/v2/internal/ui/session"
 	"miniflux.app/v2/internal/ui/view"
@@ -21,13 +21,13 @@ var urlRe = regexp.MustCompile(`(?i)(?:https?://)?[0-9a-z.]+[.][a-z]+(?::[0-9]+)
 func (h *handler) bookmarklet(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	categories, err := h.store.Categories(user.ID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -55,5 +55,5 @@ func (h *handler) bookmarklet(w http.ResponseWriter, r *http.Request) {
 	view.Set("defaultUserAgent", config.Opts.HTTPClientUserAgent())
 	view.Set("hasProxyConfigured", config.Opts.HasHTTPClientProxyURLConfigured())
 
-	html.OK(w, r, view.Render("add_subscription"))
+	response.HTML(w, r, view.Render("add_subscription"))
 }

+ 6 - 6
internal/ui/subscription_choose.go

@@ -8,7 +8,7 @@ import (
 
 	"miniflux.app/v2/internal/config"
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/model"
 	feedHandler "miniflux.app/v2/internal/reader/handler"
@@ -20,13 +20,13 @@ import (
 func (h *handler) showChooseSubscriptionPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	categories, err := h.store.Categories(user.ID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -43,7 +43,7 @@ func (h *handler) showChooseSubscriptionPage(w http.ResponseWriter, r *http.Requ
 	if validationErr := subscriptionForm.Validate(); validationErr != nil {
 		view.Set("form", subscriptionForm)
 		view.Set("errorMessage", validationErr.Translate(user.Language))
-		html.OK(w, r, view.Render("add_subscription"))
+		response.HTML(w, r, view.Render("add_subscription"))
 		return
 	}
 
@@ -71,9 +71,9 @@ func (h *handler) showChooseSubscriptionPage(w http.ResponseWriter, r *http.Requ
 	if localizedError != nil {
 		view.Set("form", subscriptionForm)
 		view.Set("errorMessage", localizedError.Translate(user.Language))
-		html.OK(w, r, view.Render("add_subscription"))
+		response.HTML(w, r, view.Render("add_subscription"))
 		return
 	}
 
-	html.Redirect(w, r, route.Path(h.router, "feedEntries", "feedID", feed.ID))
+	response.HTMLRedirect(w, r, route.Path(h.router, "feedEntries", "feedID", feed.ID))
 }

+ 11 - 11
internal/ui/subscription_submit.go

@@ -8,7 +8,7 @@ import (
 
 	"miniflux.app/v2/internal/config"
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/locale"
 	"miniflux.app/v2/internal/model"
@@ -24,13 +24,13 @@ import (
 func (h *handler) submitSubscription(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	categories, err := h.store.Categories(user.ID)
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -48,7 +48,7 @@ func (h *handler) submitSubscription(w http.ResponseWriter, r *http.Request) {
 	if validationErr := subscriptionForm.Validate(); validationErr != nil {
 		v.Set("form", subscriptionForm)
 		v.Set("errorMessage", validationErr.Translate(user.Language))
-		html.OK(w, r, v.Render("add_subscription"))
+		response.HTML(w, r, v.Render("add_subscription"))
 		return
 	}
 
@@ -80,7 +80,7 @@ func (h *handler) submitSubscription(w http.ResponseWriter, r *http.Request) {
 	if localizedError != nil {
 		v.Set("form", subscriptionForm)
 		v.Set("errorMessage", localizedError.Translate(user.Language))
-		html.OK(w, r, v.Render("add_subscription"))
+		response.HTML(w, r, v.Render("add_subscription"))
 		return
 	}
 
@@ -89,7 +89,7 @@ func (h *handler) submitSubscription(w http.ResponseWriter, r *http.Request) {
 	case n == 0:
 		v.Set("form", subscriptionForm)
 		v.Set("errorMessage", locale.NewLocalizedError("error.subscription_not_found").Translate(user.Language))
-		html.OK(w, r, v.Render("add_subscription"))
+		response.HTML(w, r, v.Render("add_subscription"))
 	case n == 1 && subscriptionFinder.IsFeedAlreadyDownloaded():
 		feed, localizedError := feedHandler.CreateFeedFromSubscriptionDiscovery(h.store, user.ID, &model.FeedCreationRequestFromSubscriptionDiscovery{
 			Content:      subscriptionFinder.FeedResponseInfo().Content,
@@ -120,11 +120,11 @@ func (h *handler) submitSubscription(w http.ResponseWriter, r *http.Request) {
 		if localizedError != nil {
 			v.Set("form", subscriptionForm)
 			v.Set("errorMessage", localizedError.Translate(user.Language))
-			html.OK(w, r, v.Render("add_subscription"))
+			response.HTML(w, r, v.Render("add_subscription"))
 			return
 		}
 
-		html.Redirect(w, r, route.Path(h.router, "feedEntries", "feedID", feed.ID))
+		response.HTMLRedirect(w, r, route.Path(h.router, "feedEntries", "feedID", feed.ID))
 	case n == 1 && !subscriptionFinder.IsFeedAlreadyDownloaded():
 		feed, localizedError := feedHandler.CreateFeed(h.store, user.ID, &model.FeedCreationRequest{
 			CategoryID:                  subscriptionForm.CategoryID,
@@ -150,11 +150,11 @@ func (h *handler) submitSubscription(w http.ResponseWriter, r *http.Request) {
 		if localizedError != nil {
 			v.Set("form", subscriptionForm)
 			v.Set("errorMessage", localizedError.Translate(user.Language))
-			html.OK(w, r, v.Render("add_subscription"))
+			response.HTML(w, r, v.Render("add_subscription"))
 			return
 		}
 
-		html.Redirect(w, r, route.Path(h.router, "feedEntries", "feedID", feed.ID))
+		response.HTMLRedirect(w, r, route.Path(h.router, "feedEntries", "feedID", feed.ID))
 	case n > 1:
 		view := view.New(h.tpl, r, sess)
 		view.Set("subscriptions", subscriptions)
@@ -165,6 +165,6 @@ func (h *handler) submitSubscription(w http.ResponseWriter, r *http.Request) {
 		view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(user.ID))
 		view.Set("hasProxyConfigured", config.Opts.HasHTTPClientProxyURLConfigured())
 
-		html.OK(w, r, view.Render("choose_subscription"))
+		response.HTML(w, r, view.Render("choose_subscription"))
 	}
 }

+ 6 - 6
internal/ui/tag_entries_all.go

@@ -8,7 +8,7 @@ import (
 	"net/url"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/ui/session"
@@ -18,13 +18,13 @@ import (
 func (h *handler) showTagEntriesAllPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	tagName, err := url.PathUnescape(request.RouteStringParam(r, "tagName"))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -40,13 +40,13 @@ func (h *handler) showTagEntriesAllPage(w http.ResponseWriter, r *http.Request)
 
 	entries, err := builder.GetEntries()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	count, err := builder.CountEntries()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -62,5 +62,5 @@ func (h *handler) showTagEntriesAllPage(w http.ResponseWriter, r *http.Request)
 	view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID))
 	view.Set("showOnlyUnreadEntries", false)
 
-	html.OK(w, r, view.Render("tag_entries"))
+	response.HTML(w, r, view.Render("tag_entries"))
 }

+ 5 - 5
internal/ui/unread_entries.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/ui/session"
@@ -17,7 +17,7 @@ import (
 func (h *handler) showUnreadPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -27,7 +27,7 @@ func (h *handler) showUnreadPage(w http.ResponseWriter, r *http.Request) {
 	builder.WithGloballyVisible()
 	countUnread, err := builder.CountEntries()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -44,7 +44,7 @@ func (h *handler) showUnreadPage(w http.ResponseWriter, r *http.Request) {
 	builder.WithGloballyVisible()
 	entries, err := builder.GetEntries()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -58,5 +58,5 @@ func (h *handler) showUnreadPage(w http.ResponseWriter, r *http.Request) {
 	view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(user.ID))
 	view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID))
 
-	html.OK(w, r, view.Render("unread_entries"))
+	response.HTML(w, r, view.Render("unread_entries"))
 }

+ 10 - 10
internal/ui/unread_entry_category.go

@@ -7,7 +7,7 @@ import (
 	"net/http"
 
 	"miniflux.app/v2/internal/http/request"
-	"miniflux.app/v2/internal/http/response/html"
+	"miniflux.app/v2/internal/http/response"
 	"miniflux.app/v2/internal/http/route"
 	"miniflux.app/v2/internal/model"
 	"miniflux.app/v2/internal/storage"
@@ -18,7 +18,7 @@ import (
 func (h *handler) showUnreadCategoryEntryPage(w http.ResponseWriter, r *http.Request) {
 	user, err := h.store.UserByID(request.UserID(r))
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -32,19 +32,19 @@ func (h *handler) showUnreadCategoryEntryPage(w http.ResponseWriter, r *http.Req
 
 	entry, err := builder.GetEntry()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
 	if entry == nil {
-		html.NotFound(w, r)
+		response.HTMLNotFound(w, r)
 		return
 	}
 
 	if entry.ShouldMarkAsReadOnView(user) {
 		err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead)
 		if err != nil {
-			html.ServerError(w, r, err)
+			response.HTMLServerError(w, r, err)
 			return
 		}
 
@@ -58,14 +58,14 @@ func (h *handler) showUnreadCategoryEntryPage(w http.ResponseWriter, r *http.Req
 	if entry.Status == model.EntryStatusRead {
 		err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusUnread)
 		if err != nil {
-			html.ServerError(w, r, err)
+			response.HTMLServerError(w, r, err)
 			return
 		}
 	}
 
 	prevEntry, nextEntry, err := entryPaginationBuilder.Entries()
 	if err != nil {
-		html.ServerError(w, r, err)
+		response.HTMLServerError(w, r, err)
 		return
 	}
 
@@ -83,13 +83,13 @@ func (h *handler) showUnreadCategoryEntryPage(w http.ResponseWriter, r *http.Req
 	if entry.Status == model.EntryStatusRead {
 		err = h.store.SetEntriesStatus(user.ID, []int64{entry.ID}, model.EntryStatusRead)
 		if err != nil {
-			html.ServerError(w, r, err)
+			response.HTMLServerError(w, r, err)
 			return
 		}
 	}
 
 	if user.AlwaysOpenExternalLinks {
-		html.Redirect(w, r, entry.URL)
+		response.HTMLRedirect(w, r, entry.URL)
 		return
 	}
 
@@ -106,5 +106,5 @@ func (h *handler) showUnreadCategoryEntryPage(w http.ResponseWriter, r *http.Req
 	view.Set("countErrorFeeds", h.store.CountUserFeedsWithErrors(user.ID))
 	view.Set("hasSaveEntry", h.store.HasSaveEntry(user.ID))
 
-	html.OK(w, r, view.Render("entry"))
+	response.HTML(w, r, view.Render("entry"))
 }

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.