init push

This commit is contained in:
2026-07-16 22:16:45 +07:00
commit 8b068bdb10
1021 changed files with 332816 additions and 0 deletions

View File

@@ -0,0 +1,93 @@
package handlers
import (
"encoding/json"
"errors"
"fmt"
"log/slog"
"strings"
"github.com/gofiber/fiber/v2"
"wucher/internal/shared/pkg/apperrorsx"
"wucher/internal/transport/http/jsonapi"
)
// bodyParseError maps a request-body JSON parse error to a precise app error. It returns
// a non-nil *AppError only for a data-type mismatch (INVALID_DATA_TYPE, pointing at the
// offending field); for any other parse error it returns nil so the caller can fall back
// to its module-specific invalid-json error.
func bodyParseError(err error) *apperrorsx.AppError {
var typeErr *json.UnmarshalTypeError
if !errors.As(err, &typeErr) {
return nil
}
appErr := apperrorsx.New(apperrorsx.ErrInvalidDataType).WithCause(err)
if typeErr.Field != "" {
appErr.Source = &jsonapi.ErrorSource{Pointer: "/" + strings.ReplaceAll(typeErr.Field, ".", "/")}
appErr.Message = fmt.Sprintf("field %q expects type %s", typeErr.Field, typeErr.Type.String())
} else {
appErr.Message = fmt.Sprintf("expected type %s", typeErr.Type.String())
}
return appErr
}
// writeBodyParseError writes a precise INVALID_DATA_TYPE error for a data-type mismatch,
// or falls back to the given module invalid-json def for any other parse error.
func writeBodyParseError(c *fiber.Ctx, err error, fallback apperrorsx.Def) error {
if appErr := bodyParseError(err); appErr != nil {
return apperrorsx.Write(c, appErr)
}
return apperrorsx.Write(c, apperrorsx.New(fallback).WithCause(err))
}
func writeInternalHandlerError(c *fiber.Ctx, operation string, err error) error {
return writeInternalHandlerErrorWithStatus(c, fiber.StatusInternalServerError, operation, err)
}
func writeHandlerErrorDef(c *fiber.Ctx, operation string, def apperrorsx.Def, err error) error {
logHandlerError(c, operation, def.Status, def.Code, def.ErrorCode, err)
appErr := apperrorsx.New(def)
if err != nil {
appErr.Cause = err
}
return apperrorsx.Write(c, appErr)
}
func writeHandlerErrorDefWithStatus(c *fiber.Ctx, operation string, status int, def apperrorsx.Def, err error) error {
logHandlerError(c, operation, status, def.Code, def.ErrorCode, err)
appErr := apperrorsx.New(def)
appErr.Status = status
if err != nil {
appErr.Cause = err
}
return apperrorsx.Write(c, appErr)
}
func writeInternalHandlerErrorWithStatus(c *fiber.Ctx, status int, operation string, err error) error {
logHandlerError(c, operation, status, apperrorsx.CodeInternalServerError, apperrorsx.ErrInternal.ErrorCode, err)
appErr := apperrorsx.New(apperrorsx.ErrInternal)
appErr.Status = status
return apperrorsx.Write(c, appErr)
}
func safeInternalDetail() string {
return apperrorsx.ErrInternal.Message
}
func logHandlerError(c *fiber.Ctx, operation string, status int, code, errorCode string, err error) {
args := []any{
slog.String("request_id", requestIDFromCtx(c)),
slog.String("operation", strings.TrimSpace(operation)),
slog.Int("status", status),
slog.String("code", strings.TrimSpace(code)),
slog.String("error_code", strings.TrimSpace(errorCode)),
slog.String("path", c.Path()),
slog.Any("cause", err),
}
if status >= fiber.StatusInternalServerError {
slog.ErrorContext(c.UserContext(), "handler error", args...)
return
}
slog.WarnContext(c.UserContext(), "handler error", args...)
}