142 lines
3.4 KiB
Go
142 lines
3.4 KiB
Go
package ginext
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/rs/zerolog/log"
|
|
"gogs.mikescher.com/BlackForestBytes/goext/ginext/apierr"
|
|
json "gogs.mikescher.com/BlackForestBytes/goext/gojson"
|
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
|
"runtime/debug"
|
|
"strings"
|
|
)
|
|
|
|
type HTTPResponse interface {
|
|
Write(g *gin.Context)
|
|
}
|
|
|
|
type jsonHTTPResponse struct {
|
|
statusCode int
|
|
data any
|
|
}
|
|
|
|
func (j jsonHTTPResponse) Write(g *gin.Context) {
|
|
g.Render(j.statusCode, json.GoJsonRender{Data: j.data, NilSafeSlices: true, NilSafeMaps: true})
|
|
}
|
|
|
|
type emptyHTTPResponse struct {
|
|
statusCode int
|
|
}
|
|
|
|
func (j emptyHTTPResponse) Write(g *gin.Context) {
|
|
g.Status(j.statusCode)
|
|
}
|
|
|
|
type textHTTPResponse struct {
|
|
statusCode int
|
|
data string
|
|
}
|
|
|
|
func (j textHTTPResponse) Write(g *gin.Context) {
|
|
g.String(j.statusCode, "%s", j.data)
|
|
}
|
|
|
|
type dataHTTPResponse struct {
|
|
statusCode int
|
|
data []byte
|
|
contentType string
|
|
}
|
|
|
|
func (j dataHTTPResponse) Write(g *gin.Context) {
|
|
g.Data(j.statusCode, j.contentType, j.data)
|
|
}
|
|
|
|
type fileHTTPResponse struct {
|
|
mimetype string
|
|
filepath string
|
|
filename *string
|
|
}
|
|
|
|
func (j fileHTTPResponse) Write(g *gin.Context) {
|
|
g.Header("Content-Type", j.mimetype) // if we don't set it here gin does weird file-sniffing later...
|
|
if j.filename != nil {
|
|
g.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", *j.filename))
|
|
|
|
}
|
|
g.File(j.filepath)
|
|
}
|
|
|
|
func Status(sc int) HTTPResponse {
|
|
return &emptyHTTPResponse{statusCode: sc}
|
|
}
|
|
|
|
func JSON(sc int, data any) HTTPResponse {
|
|
return &jsonHTTPResponse{statusCode: sc, data: data}
|
|
}
|
|
|
|
func Data(sc int, contentType string, data []byte) HTTPResponse {
|
|
return &dataHTTPResponse{statusCode: sc, contentType: contentType, data: data}
|
|
}
|
|
|
|
func Text(sc int, data string) HTTPResponse {
|
|
return &textHTTPResponse{statusCode: sc, data: data}
|
|
}
|
|
|
|
func File(mimetype string, filepath string) HTTPResponse {
|
|
return &fileHTTPResponse{mimetype: mimetype, filepath: filepath}
|
|
}
|
|
|
|
func Download(mimetype string, filepath string, filename string) HTTPResponse {
|
|
return &fileHTTPResponse{mimetype: mimetype, filepath: filepath, filename: &filename}
|
|
}
|
|
|
|
func APIError(g *gin.Context, errcode apierr.APIErrorCode, msg string, e error) HTTPResponse {
|
|
return createApiError(g, errcode, msg, e)
|
|
}
|
|
|
|
func NotImplemented(g *gin.Context) HTTPResponse {
|
|
return createApiError(g, apierr.NotImplemented, "", nil)
|
|
}
|
|
|
|
func createApiError(g *gin.Context, errcode apierr.APIErrorCode, msg string, e error) HTTPResponse {
|
|
reqUri := ""
|
|
if g != nil && g.Request != nil {
|
|
reqUri = g.Request.Method + " :: " + g.Request.RequestURI
|
|
}
|
|
|
|
log.Error().
|
|
Str("errorcode.key", errcode.Key).
|
|
Int("errcode.status", errcode.HTTPStatusCode).
|
|
Str("uri", reqUri).
|
|
AnErr("err", e).
|
|
Stack().
|
|
Msg(msg)
|
|
|
|
var fapiMessage *string = nil
|
|
if v, ok := e.(interface{ FAPIMessage() string }); ok {
|
|
fapiMessage = langext.Ptr(v.FAPIMessage())
|
|
}
|
|
|
|
if g.GetBool("__returnRawErrors") {
|
|
return &jsonHTTPResponse{
|
|
statusCode: errcode.HTTPStatusCode,
|
|
data: extAPIError{
|
|
ErrorCode: errcode.Key,
|
|
Message: msg,
|
|
RawError: langext.Ptr(langext.Conditional(e == nil, "", fmt.Sprintf("%+v", e))),
|
|
FAPIErrorMessage: fapiMessage,
|
|
Trace: strings.Split(string(debug.Stack()), "\n"),
|
|
},
|
|
}
|
|
} else {
|
|
return &jsonHTTPResponse{
|
|
statusCode: errcode.HTTPStatusCode,
|
|
data: apiError{
|
|
ErrorCode: errcode.Key,
|
|
Message: msg,
|
|
FAPIErrorMessage: fapiMessage,
|
|
},
|
|
}
|
|
}
|
|
}
|