2023-06-19 23:42:47 +02:00
|
|
|
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
2018-04-30 01:35:04 +02:00
|
|
|
|
2023-08-11 04:46:45 +02:00
|
|
|
package json // import "miniflux.app/v2/internal/http/response/json"
|
2018-04-30 01:35:04 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
2023-09-25 01:32:09 +02:00
|
|
|
"log/slog"
|
2018-04-30 01:35:04 +02:00
|
|
|
"net/http"
|
|
|
|
|
2023-09-25 01:32:09 +02:00
|
|
|
"miniflux.app/v2/internal/http/request"
|
2023-08-11 04:46:45 +02:00
|
|
|
"miniflux.app/v2/internal/http/response"
|
2018-04-30 01:35:04 +02:00
|
|
|
)
|
|
|
|
|
2018-11-03 20:03:06 +01:00
|
|
|
const contentTypeHeader = `application/json`
|
|
|
|
|
2018-10-08 03:42:43 +02:00
|
|
|
// OK creates a new JSON response with a 200 status code.
|
|
|
|
func OK(w http.ResponseWriter, r *http.Request, body interface{}) {
|
|
|
|
builder := response.New(w, r)
|
2018-11-03 20:03:06 +01:00
|
|
|
builder.WithHeader("Content-Type", contentTypeHeader)
|
2018-10-08 03:42:43 +02:00
|
|
|
builder.WithBody(toJSON(body))
|
|
|
|
builder.Write()
|
2018-04-30 01:35:04 +02:00
|
|
|
}
|
|
|
|
|
2018-10-08 03:42:43 +02:00
|
|
|
// Created sends a created response to the client.
|
|
|
|
func Created(w http.ResponseWriter, r *http.Request, body interface{}) {
|
|
|
|
builder := response.New(w, r)
|
|
|
|
builder.WithStatus(http.StatusCreated)
|
2018-11-03 20:03:06 +01:00
|
|
|
builder.WithHeader("Content-Type", contentTypeHeader)
|
2018-10-08 03:42:43 +02:00
|
|
|
builder.WithBody(toJSON(body))
|
|
|
|
builder.Write()
|
2018-04-30 01:35:04 +02:00
|
|
|
}
|
|
|
|
|
2018-10-08 03:42:43 +02:00
|
|
|
// 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)
|
2018-11-03 20:03:06 +01:00
|
|
|
builder.WithHeader("Content-Type", contentTypeHeader)
|
2018-10-08 03:42:43 +02:00
|
|
|
builder.Write()
|
2018-04-30 01:35:04 +02:00
|
|
|
}
|
|
|
|
|
2023-07-31 00:33:56 +02:00
|
|
|
func Accepted(w http.ResponseWriter, r *http.Request) {
|
|
|
|
builder := response.New(w, r)
|
|
|
|
builder.WithStatus(http.StatusAccepted)
|
|
|
|
builder.WithHeader("Content-Type", contentTypeHeader)
|
|
|
|
builder.Write()
|
|
|
|
}
|
|
|
|
|
2018-10-08 03:42:43 +02:00
|
|
|
// ServerError sends an internal error to the client.
|
|
|
|
func ServerError(w http.ResponseWriter, r *http.Request, err error) {
|
2023-09-25 01:32:09 +02:00
|
|
|
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),
|
|
|
|
),
|
|
|
|
)
|
2018-10-08 03:42:43 +02:00
|
|
|
|
|
|
|
builder := response.New(w, r)
|
|
|
|
builder.WithStatus(http.StatusInternalServerError)
|
2018-11-03 20:03:06 +01:00
|
|
|
builder.WithHeader("Content-Type", contentTypeHeader)
|
2018-10-08 03:42:43 +02:00
|
|
|
builder.WithBody(toJSONError(err))
|
|
|
|
builder.Write()
|
2018-04-30 01:35:04 +02:00
|
|
|
}
|
|
|
|
|
2018-10-08 03:42:43 +02:00
|
|
|
// BadRequest sends a bad request error to the client.
|
|
|
|
func BadRequest(w http.ResponseWriter, r *http.Request, err error) {
|
2023-09-25 01:32:09 +02:00
|
|
|
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),
|
|
|
|
),
|
|
|
|
)
|
2018-04-30 01:35:04 +02:00
|
|
|
|
2018-10-08 03:42:43 +02:00
|
|
|
builder := response.New(w, r)
|
|
|
|
builder.WithStatus(http.StatusBadRequest)
|
2018-11-03 20:03:06 +01:00
|
|
|
builder.WithHeader("Content-Type", contentTypeHeader)
|
2018-10-08 03:42:43 +02:00
|
|
|
builder.WithBody(toJSONError(err))
|
|
|
|
builder.Write()
|
2018-04-30 01:35:04 +02:00
|
|
|
}
|
|
|
|
|
2018-10-08 03:42:43 +02:00
|
|
|
// Unauthorized sends a not authorized error to the client.
|
|
|
|
func Unauthorized(w http.ResponseWriter, r *http.Request) {
|
2023-09-25 01:32:09 +02:00
|
|
|
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),
|
|
|
|
),
|
|
|
|
)
|
2018-04-30 01:35:04 +02:00
|
|
|
|
2018-10-08 03:42:43 +02:00
|
|
|
builder := response.New(w, r)
|
|
|
|
builder.WithStatus(http.StatusUnauthorized)
|
2018-11-03 20:03:06 +01:00
|
|
|
builder.WithHeader("Content-Type", contentTypeHeader)
|
2023-07-31 00:33:56 +02:00
|
|
|
builder.WithBody(toJSONError(errors.New("access unauthorized")))
|
2018-10-08 03:42:43 +02:00
|
|
|
builder.Write()
|
2018-04-30 01:35:04 +02:00
|
|
|
}
|
|
|
|
|
2018-10-08 03:42:43 +02:00
|
|
|
// Forbidden sends a forbidden error to the client.
|
|
|
|
func Forbidden(w http.ResponseWriter, r *http.Request) {
|
2023-09-25 01:32:09 +02:00
|
|
|
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),
|
|
|
|
),
|
|
|
|
)
|
2018-04-30 01:35:04 +02:00
|
|
|
|
2018-10-08 03:42:43 +02:00
|
|
|
builder := response.New(w, r)
|
|
|
|
builder.WithStatus(http.StatusForbidden)
|
2018-11-03 20:03:06 +01:00
|
|
|
builder.WithHeader("Content-Type", contentTypeHeader)
|
2023-07-31 00:33:56 +02:00
|
|
|
builder.WithBody(toJSONError(errors.New("access forbidden")))
|
2018-10-08 03:42:43 +02:00
|
|
|
builder.Write()
|
2018-04-30 01:35:04 +02:00
|
|
|
}
|
|
|
|
|
2018-10-08 03:42:43 +02:00
|
|
|
// NotFound sends a page not found error to the client.
|
|
|
|
func NotFound(w http.ResponseWriter, r *http.Request) {
|
2023-09-25 01:32:09 +02:00
|
|
|
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),
|
|
|
|
),
|
|
|
|
)
|
2018-10-08 03:42:43 +02:00
|
|
|
|
|
|
|
builder := response.New(w, r)
|
|
|
|
builder.WithStatus(http.StatusNotFound)
|
2018-11-03 20:03:06 +01:00
|
|
|
builder.WithHeader("Content-Type", contentTypeHeader)
|
2023-07-31 00:33:56 +02:00
|
|
|
builder.WithBody(toJSONError(errors.New("resource not found")))
|
2018-10-08 03:42:43 +02:00
|
|
|
builder.Write()
|
2018-04-30 01:35:04 +02:00
|
|
|
}
|
|
|
|
|
2018-10-08 03:42:43 +02:00
|
|
|
func toJSONError(err error) []byte {
|
2018-04-30 01:35:04 +02:00
|
|
|
type errorMsg struct {
|
|
|
|
ErrorMessage string `json:"error_message"`
|
|
|
|
}
|
|
|
|
|
2018-10-08 03:42:43 +02:00
|
|
|
return toJSON(errorMsg{ErrorMessage: err.Error()})
|
2018-04-30 01:35:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func toJSON(v interface{}) []byte {
|
|
|
|
b, err := json.Marshal(v)
|
|
|
|
if err != nil {
|
2023-09-25 01:32:09 +02:00
|
|
|
slog.Error("Unable to marshal JSON response", slog.Any("error", err))
|
2018-04-30 01:35:04 +02:00
|
|
|
return []byte("")
|
|
|
|
}
|
|
|
|
|
|
|
|
return b
|
|
|
|
}
|