refactored for maintainability
This commit is contained in:
@@ -2,152 +2,15 @@ package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"git.haelnorr.com/h/golib/hws"
|
||||
"git.haelnorr.com/h/oslstats/internal/notify"
|
||||
"git.haelnorr.com/h/oslstats/internal/throw"
|
||||
"github.com/a-h/templ"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// throwError is a generic helper that all throw* functions use internally
|
||||
func throwError(
|
||||
s *hws.Server,
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
statusCode int,
|
||||
msg string,
|
||||
err error,
|
||||
level hws.ErrorLevel,
|
||||
) {
|
||||
s.ThrowError(w, r, hws.HWSError{
|
||||
StatusCode: statusCode,
|
||||
Message: msg,
|
||||
Error: err,
|
||||
Level: level,
|
||||
RenderErrorPage: true, // throw* family always renders error pages
|
||||
})
|
||||
}
|
||||
|
||||
// throwInternalServiceError handles 500 errors (server failures)
|
||||
func throwInternalServiceError(
|
||||
s *hws.Server,
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
msg string,
|
||||
err error,
|
||||
) {
|
||||
throwError(s, w, r, http.StatusInternalServerError, msg, err, hws.ErrorERROR)
|
||||
}
|
||||
|
||||
// throwServiceUnavailable handles 503 errors
|
||||
func throwServiceUnavailable(
|
||||
s *hws.Server,
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
msg string,
|
||||
err error,
|
||||
) {
|
||||
throwError(s, w, r, http.StatusServiceUnavailable, msg, err, hws.ErrorERROR)
|
||||
}
|
||||
|
||||
// throwBadRequest handles 400 errors (malformed requests)
|
||||
func throwBadRequest(
|
||||
s *hws.Server,
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
msg string,
|
||||
err error,
|
||||
) {
|
||||
throwError(s, w, r, http.StatusBadRequest, msg, err, hws.ErrorDEBUG)
|
||||
}
|
||||
|
||||
// throwForbidden handles 403 errors (normal permission denials)
|
||||
func throwForbidden(
|
||||
s *hws.Server,
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
msg string,
|
||||
err error,
|
||||
) {
|
||||
throwError(s, w, r, http.StatusForbidden, msg, err, hws.ErrorDEBUG)
|
||||
}
|
||||
|
||||
// throwForbiddenSecurity handles 403 errors for security events (uses WARN level)
|
||||
func throwForbiddenSecurity(
|
||||
s *hws.Server,
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
msg string,
|
||||
err error,
|
||||
) {
|
||||
throwError(s, w, r, http.StatusForbidden, msg, err, hws.ErrorWARN)
|
||||
}
|
||||
|
||||
// throwUnauthorized handles 401 errors (not authenticated)
|
||||
func throwUnauthorized(
|
||||
s *hws.Server,
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
msg string,
|
||||
err error,
|
||||
) {
|
||||
throwError(s, w, r, http.StatusUnauthorized, msg, err, hws.ErrorDEBUG)
|
||||
}
|
||||
|
||||
// throwUnauthorizedSecurity handles 401 errors for security events (uses WARN level)
|
||||
func throwUnauthorizedSecurity(
|
||||
s *hws.Server,
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
msg string,
|
||||
err error,
|
||||
) {
|
||||
throwError(s, w, r, http.StatusUnauthorized, msg, err, hws.ErrorWARN)
|
||||
}
|
||||
|
||||
// throwNotFound handles 404 errors
|
||||
func throwNotFound(
|
||||
s *hws.Server,
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
path string,
|
||||
) {
|
||||
msg := fmt.Sprintf("The requested resource was not found: %s", path)
|
||||
err := errors.New("Resource not found")
|
||||
throwError(s, w, r, http.StatusNotFound, msg, err, hws.ErrorDEBUG)
|
||||
}
|
||||
|
||||
// ErrorDetails contains structured error information for WebSocket error modals
|
||||
type ErrorDetails struct {
|
||||
Code int `json:"code"`
|
||||
Stacktrace string `json:"stacktrace"`
|
||||
}
|
||||
|
||||
// formatErrorDetails extracts and formats error details from wrapped errors
|
||||
func formatErrorDetails(err error) string {
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
// Use %+v format to get stack trace from github.com/pkg/errors
|
||||
return fmt.Sprintf("%+v", err)
|
||||
}
|
||||
|
||||
// SerializeErrorDetails creates a JSON string with code and stacktrace
|
||||
// This is exported so it can be used when creating error notifications
|
||||
func SerializeErrorDetails(code int, err error) string {
|
||||
details := ErrorDetails{
|
||||
Code: code,
|
||||
Stacktrace: formatErrorDetails(err),
|
||||
}
|
||||
jsonData, jsonErr := json.Marshal(details)
|
||||
if jsonErr != nil {
|
||||
// Fallback if JSON encoding fails
|
||||
return fmt.Sprintf(`{"code":%d,"stacktrace":"Failed to serialize error"}`, code)
|
||||
}
|
||||
return string(jsonData)
|
||||
}
|
||||
|
||||
// parseErrorDetails extracts code and stacktrace from JSON Details field
|
||||
// Returns (code, stacktrace). If parsing fails, returns (500, original details string)
|
||||
func parseErrorDetails(details string) (int, string) {
|
||||
@@ -155,7 +18,7 @@ func parseErrorDetails(details string) (int, string) {
|
||||
return 500, ""
|
||||
}
|
||||
|
||||
var errDetails ErrorDetails
|
||||
var errDetails notify.ErrorDetails
|
||||
err := json.Unmarshal([]byte(details), &errDetails)
|
||||
if err != nil {
|
||||
// Not JSON or malformed - treat as plain stacktrace with default code
|
||||
@@ -168,6 +31,6 @@ func parseErrorDetails(details string) (int, string) {
|
||||
func renderSafely(page templ.Component, s *hws.Server, r *http.Request, w http.ResponseWriter) {
|
||||
err := page.Render(r.Context(), w)
|
||||
if err != nil {
|
||||
throwInternalServiceError(s, w, r, "Failed to render page", errors.Wrap(err, "page."))
|
||||
throw.InternalServiceError(s, w, r, "Failed to render page", errors.Wrap(err, "page."))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user