Update all middleware functions to use *echo.Context (pointer) instead of echo.Context (value) as required by Echo v5. Changes in device_auth.go: - Update DeviceAuthMiddleware() signature (line 38) - Update validateDeviceAuth() signature (line 170) - Update RequireDeviceAuth() signature (line 212) Changes in error_handler.go: - Update RespondWithError() signature (line 44) - Update RespondWithHTTPError() signature (line 69) - Update WrapHandler() to accept *echo.Context (line 82) - Fix context passing in WrapHandler() (c is already pointer) Changes in rate_limiter.go: - Update RateLimiterMiddleware() signature (line 102) Changes in request_tracing.go: - Update RequestTracingMiddleware() signature (line 48) - Fix Response() dereference for v5 API (line 264) - Use *c.Response() to get http.ResponseWriter Changes in security.go: - Update SecurityHeadersMiddleware() signature (line 14) Changes in device_auth_test.go: - Update test helper signatures Changes in middleware_test.go: - Remove unused import All middleware now properly implements Echo v5's pointer-based context pattern.
123 lines
3.2 KiB
Go
123 lines
3.2 KiB
Go
package middleware
|
|
|
|
import (
|
|
"bookhoard/internal/config"
|
|
"bytes"
|
|
"encoding/json"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/labstack/echo/v5"
|
|
)
|
|
|
|
type responseWriter struct {
|
|
http.ResponseWriter
|
|
size int
|
|
}
|
|
|
|
func (r *responseWriter) Write(b []byte) (int, error) {
|
|
size, err := r.ResponseWriter.Write(b)
|
|
r.size += size
|
|
return size, err
|
|
}
|
|
|
|
type RequestLogEntry struct {
|
|
RequestID string `json:"request_id"`
|
|
Timestamp time.Time `json:"timestamp"`
|
|
Method string `json:"method"`
|
|
Path string `json:"path"`
|
|
QueryParams map[string]string `json:"query_params,omitempty"`
|
|
Headers map[string]string `json:"headers,omitempty"`
|
|
Body interface{} `json:"body,omitempty"`
|
|
UserID string `json:"user_id,omitempty"`
|
|
UserRole string `json:"user_role,omitempty"`
|
|
UserEmail string `json:"user_email,omitempty"`
|
|
RemoteAddr string `json:"remote_addr"`
|
|
UserAgent string `json:"user_agent,omitempty"`
|
|
Duration time.Duration `json:"duration"`
|
|
StatusCode int `json:"status_code"`
|
|
ResponseSize int64 `json:"response_size"`
|
|
Error string `json:"error,omitempty"`
|
|
}
|
|
|
|
func RequestTracingMiddleware(cfg *config.Config) echo.MiddlewareFunc {
|
|
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
|
return func(c *echo.Context) error {
|
|
start := time.Now()
|
|
|
|
requestID := c.Response().Header().Get(echo.HeaderXRequestID)
|
|
if requestID == "" {
|
|
requestID = uuid.New().String()
|
|
c.Response().Header().Set(echo.HeaderXRequestID, requestID)
|
|
}
|
|
|
|
recorder := &responseWriter{
|
|
ResponseWriter: c.Response(),
|
|
}
|
|
c.SetResponse(recorder)
|
|
|
|
var body interface{}
|
|
if c.Request().Body != nil && c.Request().Method != "GET" {
|
|
bodyBytes, _ := io.ReadAll(c.Request().Body)
|
|
c.Request().Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
|
|
|
|
if len(bodyBytes) > 0 && len(bodyBytes) < 10000 {
|
|
json.Unmarshal(bodyBytes, &body)
|
|
}
|
|
}
|
|
|
|
headers := make(map[string]string)
|
|
for k, v := range c.Request().Header {
|
|
if len(v) > 0 {
|
|
headers[k] = v[0]
|
|
}
|
|
}
|
|
|
|
userID, _ := c.Get("user_id").(string)
|
|
userRole, _ := c.Get("user_role").(string)
|
|
userEmail, _ := c.Get("user_email").(string)
|
|
|
|
err := next(c)
|
|
|
|
duration := time.Since(start)
|
|
|
|
queryParams := make(map[string]string)
|
|
for k, v := range c.QueryParams() {
|
|
if len(v) > 0 {
|
|
queryParams[k] = v[0]
|
|
}
|
|
}
|
|
|
|
logEntry := RequestLogEntry{
|
|
RequestID: requestID,
|
|
Timestamp: start,
|
|
Method: c.Request().Method,
|
|
Path: c.Request().URL.Path,
|
|
QueryParams: queryParams,
|
|
Headers: headers,
|
|
Body: body,
|
|
UserID: userID,
|
|
UserRole: userRole,
|
|
UserEmail: userEmail,
|
|
RemoteAddr: c.RealIP(),
|
|
UserAgent: c.Request().UserAgent(),
|
|
Duration: duration,
|
|
StatusCode: c.Response().(*echo.Response).Status,
|
|
ResponseSize: int64(recorder.size),
|
|
}
|
|
|
|
if err != nil {
|
|
logEntry.Error = err.Error()
|
|
}
|
|
|
|
logJSON, _ := json.Marshal(logEntry)
|
|
log.Printf("[REQUEST] %s", string(logJSON))
|
|
|
|
return err
|
|
}
|
|
}
|
|
}
|