119 lines
2.4 KiB
Go
119 lines
2.4 KiB
Go
package httpapi
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"git.aiterp.net/stufflog3/stufflog3/models"
|
|
"github.com/gin-gonic/gin"
|
|
"log"
|
|
"strconv"
|
|
)
|
|
|
|
type statusError interface {
|
|
HttpStatus() (int, string, interface{})
|
|
}
|
|
|
|
func processorHandler[I any, O any](key, idKey string, callback func(ctx context.Context, id int, input I) (O, error)) gin.HandlerFunc {
|
|
return handler(key, func(c *gin.Context) (interface{}, error) {
|
|
input := new(I)
|
|
err := c.BindJSON(input)
|
|
if err != nil {
|
|
return nil, models.BadInputError{
|
|
Object: "body",
|
|
Field: "*",
|
|
Problem: "Bad JSON input:" + err.Error(),
|
|
}
|
|
}
|
|
|
|
id := 0
|
|
if idKey != "" {
|
|
id, err = reqInt(c, idKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return callback(c.Request.Context(), id, *input)
|
|
})
|
|
}
|
|
|
|
func getterHandler[O any](key, idKey string, callback func(ctx context.Context, id int) (O, error)) gin.HandlerFunc {
|
|
return handler(key, func(c *gin.Context) (interface{}, error) {
|
|
id := 0
|
|
if idKey != "" {
|
|
parsed, err := reqInt(c, idKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
id = parsed
|
|
}
|
|
|
|
return callback(c.Request.Context(), id)
|
|
})
|
|
}
|
|
|
|
func handler(key string, callback func(c *gin.Context) (interface{}, error)) gin.HandlerFunc {
|
|
type overrider interface {
|
|
OverrideJSONResponse() bool
|
|
}
|
|
|
|
return func(c *gin.Context) {
|
|
res, err := callback(c)
|
|
if err != nil {
|
|
if statusError, ok := err.(statusError); ok {
|
|
code, msg, data := statusError.HttpStatus()
|
|
c.JSON(code, Error{
|
|
Code: code,
|
|
Message: msg,
|
|
Data: data,
|
|
})
|
|
} else {
|
|
id := make([]byte, 16)
|
|
_, _ = rand.Read(id)
|
|
idHex := hex.EncodeToString(id)
|
|
|
|
log.Println("Internal server error", idHex, "triggered by:", err)
|
|
|
|
c.JSON(500, Error{
|
|
Code: 500,
|
|
Message: "Internal server error (" + idHex + ")",
|
|
Data: nil,
|
|
})
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
if o, ok := res.(overrider); ok && o.OverrideJSONResponse() {
|
|
c.JSON(200, res)
|
|
return
|
|
}
|
|
|
|
resJson := make(map[string]interface{}, 1)
|
|
resJson[key] = res
|
|
|
|
c.JSON(200, resJson)
|
|
}
|
|
}
|
|
|
|
type Error struct {
|
|
Code int `json:"statusCode"`
|
|
Message string `json:"statusMessage"`
|
|
Data interface{} `json:"errorData,omitempty"`
|
|
}
|
|
|
|
func reqInt(c *gin.Context, key string) (int, error) {
|
|
v, err := strconv.Atoi(c.Param(key))
|
|
if err != nil {
|
|
return 0, models.BadInputError{
|
|
Object: "query",
|
|
Field: key,
|
|
Problem: "Value for " + key + " must be numeric",
|
|
}
|
|
}
|
|
|
|
return v, nil
|
|
}
|