|
|
package common |
|
|
|
|
|
import ( |
|
|
"bytes" |
|
|
"errors" |
|
|
"io" |
|
|
"mime" |
|
|
"mime/multipart" |
|
|
"net/http" |
|
|
"net/url" |
|
|
"strings" |
|
|
"time" |
|
|
|
|
|
"github.com/QuantumNous/new-api/constant" |
|
|
|
|
|
"github.com/gin-gonic/gin" |
|
|
) |
|
|
|
|
|
const KeyRequestBody = "key_request_body" |
|
|
|
|
|
func GetRequestBody(c *gin.Context) ([]byte, error) { |
|
|
requestBody, _ := c.Get(KeyRequestBody) |
|
|
if requestBody != nil { |
|
|
return requestBody.([]byte), nil |
|
|
} |
|
|
requestBody, err := io.ReadAll(c.Request.Body) |
|
|
if err != nil { |
|
|
return nil, err |
|
|
} |
|
|
_ = c.Request.Body.Close() |
|
|
c.Set(KeyRequestBody, requestBody) |
|
|
return requestBody.([]byte), nil |
|
|
} |
|
|
|
|
|
func UnmarshalBodyReusable(c *gin.Context, v any) error { |
|
|
requestBody, err := GetRequestBody(c) |
|
|
if err != nil { |
|
|
return err |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
contentType := c.Request.Header.Get("Content-Type") |
|
|
if strings.HasPrefix(contentType, "application/json") { |
|
|
err = Unmarshal(requestBody, v) |
|
|
} else if strings.Contains(contentType, gin.MIMEPOSTForm) { |
|
|
err = parseFormData(requestBody, v) |
|
|
} else if strings.Contains(contentType, gin.MIMEMultipartPOSTForm) { |
|
|
err = parseMultipartFormData(c, requestBody, v) |
|
|
} else { |
|
|
|
|
|
|
|
|
} |
|
|
if err != nil { |
|
|
return err |
|
|
} |
|
|
|
|
|
c.Request.Body = io.NopCloser(bytes.NewBuffer(requestBody)) |
|
|
return nil |
|
|
} |
|
|
|
|
|
func SetContextKey(c *gin.Context, key constant.ContextKey, value any) { |
|
|
c.Set(string(key), value) |
|
|
} |
|
|
|
|
|
func GetContextKey(c *gin.Context, key constant.ContextKey) (any, bool) { |
|
|
return c.Get(string(key)) |
|
|
} |
|
|
|
|
|
func GetContextKeyString(c *gin.Context, key constant.ContextKey) string { |
|
|
return c.GetString(string(key)) |
|
|
} |
|
|
|
|
|
func GetContextKeyInt(c *gin.Context, key constant.ContextKey) int { |
|
|
return c.GetInt(string(key)) |
|
|
} |
|
|
|
|
|
func GetContextKeyBool(c *gin.Context, key constant.ContextKey) bool { |
|
|
return c.GetBool(string(key)) |
|
|
} |
|
|
|
|
|
func GetContextKeyStringSlice(c *gin.Context, key constant.ContextKey) []string { |
|
|
return c.GetStringSlice(string(key)) |
|
|
} |
|
|
|
|
|
func GetContextKeyStringMap(c *gin.Context, key constant.ContextKey) map[string]any { |
|
|
return c.GetStringMap(string(key)) |
|
|
} |
|
|
|
|
|
func GetContextKeyTime(c *gin.Context, key constant.ContextKey) time.Time { |
|
|
return c.GetTime(string(key)) |
|
|
} |
|
|
|
|
|
func GetContextKeyType[T any](c *gin.Context, key constant.ContextKey) (T, bool) { |
|
|
if value, ok := c.Get(string(key)); ok { |
|
|
if v, ok := value.(T); ok { |
|
|
return v, true |
|
|
} |
|
|
} |
|
|
var t T |
|
|
return t, false |
|
|
} |
|
|
|
|
|
func ApiError(c *gin.Context, err error) { |
|
|
c.JSON(http.StatusOK, gin.H{ |
|
|
"success": false, |
|
|
"message": err.Error(), |
|
|
}) |
|
|
} |
|
|
|
|
|
func ApiErrorMsg(c *gin.Context, msg string) { |
|
|
c.JSON(http.StatusOK, gin.H{ |
|
|
"success": false, |
|
|
"message": msg, |
|
|
}) |
|
|
} |
|
|
|
|
|
func ApiSuccess(c *gin.Context, data any) { |
|
|
c.JSON(http.StatusOK, gin.H{ |
|
|
"success": true, |
|
|
"message": "", |
|
|
"data": data, |
|
|
}) |
|
|
} |
|
|
|
|
|
func ParseMultipartFormReusable(c *gin.Context) (*multipart.Form, error) { |
|
|
requestBody, err := GetRequestBody(c) |
|
|
if err != nil { |
|
|
return nil, err |
|
|
} |
|
|
|
|
|
contentType := c.Request.Header.Get("Content-Type") |
|
|
boundary, err := parseBoundary(contentType) |
|
|
if err != nil { |
|
|
return nil, err |
|
|
} |
|
|
|
|
|
reader := multipart.NewReader(bytes.NewReader(requestBody), boundary) |
|
|
form, err := reader.ReadForm(multipartMemoryLimit()) |
|
|
if err != nil { |
|
|
return nil, err |
|
|
} |
|
|
|
|
|
|
|
|
c.Request.Body = io.NopCloser(bytes.NewBuffer(requestBody)) |
|
|
return form, nil |
|
|
} |
|
|
|
|
|
func processFormMap(formMap map[string]any, v any) error { |
|
|
jsonData, err := Marshal(formMap) |
|
|
if err != nil { |
|
|
return err |
|
|
} |
|
|
|
|
|
err = Unmarshal(jsonData, v) |
|
|
if err != nil { |
|
|
return err |
|
|
} |
|
|
|
|
|
return nil |
|
|
} |
|
|
|
|
|
func parseFormData(data []byte, v any) error { |
|
|
values, err := url.ParseQuery(string(data)) |
|
|
if err != nil { |
|
|
return err |
|
|
} |
|
|
formMap := make(map[string]any) |
|
|
for key, vals := range values { |
|
|
if len(vals) == 1 { |
|
|
formMap[key] = vals[0] |
|
|
} else { |
|
|
formMap[key] = vals |
|
|
} |
|
|
} |
|
|
|
|
|
return processFormMap(formMap, v) |
|
|
} |
|
|
|
|
|
func parseMultipartFormData(c *gin.Context, data []byte, v any) error { |
|
|
contentType := c.Request.Header.Get("Content-Type") |
|
|
boundary, err := parseBoundary(contentType) |
|
|
if err != nil { |
|
|
if errors.Is(err, errBoundaryNotFound) { |
|
|
return Unmarshal(data, v) |
|
|
} |
|
|
return err |
|
|
} |
|
|
|
|
|
reader := multipart.NewReader(bytes.NewReader(data), boundary) |
|
|
form, err := reader.ReadForm(multipartMemoryLimit()) |
|
|
if err != nil { |
|
|
return err |
|
|
} |
|
|
defer form.RemoveAll() |
|
|
formMap := make(map[string]any) |
|
|
for key, vals := range form.Value { |
|
|
if len(vals) == 1 { |
|
|
formMap[key] = vals[0] |
|
|
} else { |
|
|
formMap[key] = vals |
|
|
} |
|
|
} |
|
|
|
|
|
return processFormMap(formMap, v) |
|
|
} |
|
|
|
|
|
var errBoundaryNotFound = errors.New("multipart boundary not found") |
|
|
|
|
|
|
|
|
func parseBoundary(contentType string) (string, error) { |
|
|
if contentType == "" { |
|
|
return "", errBoundaryNotFound |
|
|
} |
|
|
|
|
|
_, params, err := mime.ParseMediaType(contentType) |
|
|
if err != nil { |
|
|
return "", err |
|
|
} |
|
|
boundary, ok := params["boundary"] |
|
|
if !ok || boundary == "" { |
|
|
return "", errBoundaryNotFound |
|
|
} |
|
|
return boundary, nil |
|
|
} |
|
|
|
|
|
|
|
|
func multipartMemoryLimit() int64 { |
|
|
limitMB := constant.MaxFileDownloadMB |
|
|
if limitMB <= 0 { |
|
|
limitMB = 32 |
|
|
} |
|
|
return int64(limitMB) << 20 |
|
|
} |
|
|
|