-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patherror.go
More file actions
137 lines (120 loc) · 3.25 KB
/
error.go
File metadata and controls
137 lines (120 loc) · 3.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package http
import (
"fmt"
"mime/multipart"
"runtime"
"github.com/gabriel-vasile/mimetype"
"github.com/gofiber/fiber/v2"
"github.com/inhies/go-bytesize"
)
// HttpError represents an HTTP error with additional context.
type HttpError struct {
Line int // Line number where the error occurred.
File string // File name where the error occurred.
Body map[string]any // Request body data (if available).
Status int // HTTP status code.
Message string // Error message.
}
// Error returns the error message as a string.
func (he HttpError) Error() string {
return he.Message
}
// NewError creates an HttpError with a message and optional status code.
// Defaults to status 500 if none is provided.
func NewError(e string, status ...int) error {
file, line, _ := realCaller()
return HttpError{
Line: line,
File: file,
Body: nil,
Status: realStatus(status...),
Message: e,
}
}
// NewFormError creates an HttpError with a message, request context, and optional status code.
// Includes request body data if available.
func NewFormError(e string, ctx *fiber.Ctx, status ...int) error {
file, line, _ := realCaller()
return HttpError{
Line: line,
File: file,
Body: extractRequestBody(ctx),
Status: realStatus(status...),
Message: e,
}
}
// extractRequestBody extracts request body data from the Fiber context.
// Handles both form data and JSON body parsing.
func extractRequestBody(ctx *fiber.Ctx) map[string]any {
if ctx == nil {
return nil
}
body := make(map[string]any)
if form, err := ctx.MultipartForm(); err == nil && form != nil {
// Extract form values
for k, v := range form.Value {
if len(v) == 1 {
body["form."+k] = v[0]
} else if len(v) > 1 {
body["form."+k] = v
} else {
body["form."+k] = nil
}
}
// Extract uploaded files
for k, files := range form.File {
values := make([]string, 0, len(files))
for _, file := range files {
size := bytesize.New(float64(file.Size))
mime := detectMime(file)
values = append(values, fmt.Sprintf("%s [%s] (%s)", file.Filename, size, mime))
}
if len(values) == 0 {
body["file."+k] = nil
} else {
body["file."+k] = values
}
}
} else {
// Parse JSON body
var form map[string]any
if err := ctx.BodyParser(&form); err != nil {
body["form"] = err.Error()
} else if len(form) == 0 {
body["form"] = nil
} else {
for k, v := range form {
body["form."+k] = v
}
}
}
return body
}
// detectMime determines the MIME type of a file.
// Returns "?" if the MIME type cannot be determined.
func detectMime(file *multipart.FileHeader) string {
f, err := file.Open()
if err != nil {
return "?"
}
defer f.Close()
if mime, err := mimetype.DetectReader(f); err == nil && mime != nil {
return mime.String()
}
return "?"
}
// realCaller retrieves the file name and line number of the caller.
func realCaller() (string, int, bool) {
if _, f, l, ok := runtime.Caller(2); ok {
return f, l, true
}
return "", 0, false
}
// realStatus validates and returns an HTTP status code.
// Defaults to 500 if the provided status is invalid.
func realStatus(statuses ...int) int {
if len(statuses) > 0 && statuses[0] > 399 && statuses[0] < 600 {
return statuses[0]
}
return 500
}