mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-10 12:00:17 +00:00
- Replace all 'any' types with proper TypeScript types throughout the codebase - Fix Record<string, any> to use specific types (AlertThresholds, unknown) - Update logger methods to use 'unknown' instead of 'any' for parameters - Fix type assertions to use proper types instead of 'as any' - Update generic type defaults from 'any' to 'unknown' - Fix WebSocket message types to use 'unknown' for optional data - Move global Toast declaration to top level to fix TypeScript errors - Comment out legacy PBS backup code that referenced non-existent fields - Ensure all code follows TypeScript standards as documented in CLAUDE.md All TypeScript compilation errors have been resolved and the codebase now adheres to strict typing standards with no 'any' types remaining.
189 lines
No EOL
5 KiB
Go
189 lines
No EOL
5 KiB
Go
package errors
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// Base error types
|
|
var (
|
|
ErrNotFound = errors.New("not found")
|
|
ErrUnauthorized = errors.New("unauthorized")
|
|
ErrForbidden = errors.New("forbidden")
|
|
ErrTimeout = errors.New("timeout")
|
|
ErrInvalidInput = errors.New("invalid input")
|
|
ErrConnectionFailed = errors.New("connection failed")
|
|
ErrInternalError = errors.New("internal error")
|
|
)
|
|
|
|
// ErrorType represents the category of error
|
|
type ErrorType string
|
|
|
|
const (
|
|
ErrorTypeConnection ErrorType = "connection"
|
|
ErrorTypeAuth ErrorType = "auth"
|
|
ErrorTypeValidation ErrorType = "validation"
|
|
ErrorTypeNotFound ErrorType = "not_found"
|
|
ErrorTypeInternal ErrorType = "internal"
|
|
ErrorTypeAPI ErrorType = "api"
|
|
ErrorTypeTimeout ErrorType = "timeout"
|
|
)
|
|
|
|
// MonitorError is a structured error for monitoring operations
|
|
type MonitorError struct {
|
|
Type ErrorType
|
|
Op string // Operation that failed (e.g., "poll_nodes", "get_vms")
|
|
Instance string // Instance name where error occurred
|
|
Node string // Node name if applicable
|
|
Err error // Underlying error
|
|
StatusCode int // HTTP status code if applicable
|
|
Timestamp time.Time
|
|
Retryable bool
|
|
}
|
|
|
|
func (e *MonitorError) Error() string {
|
|
if e.Node != "" {
|
|
return fmt.Sprintf("%s failed on %s/%s: %v", e.Op, e.Instance, e.Node, e.Err)
|
|
}
|
|
if e.Instance != "" {
|
|
return fmt.Sprintf("%s failed on %s: %v", e.Op, e.Instance, e.Err)
|
|
}
|
|
return fmt.Sprintf("%s failed: %v", e.Op, e.Err)
|
|
}
|
|
|
|
func (e *MonitorError) Unwrap() error {
|
|
return e.Err
|
|
}
|
|
|
|
// Is implements errors.Is interface
|
|
func (e *MonitorError) Is(target error) bool {
|
|
if target == nil {
|
|
return false
|
|
}
|
|
|
|
// Check base error types
|
|
switch target {
|
|
case ErrNotFound:
|
|
return e.Type == ErrorTypeNotFound
|
|
case ErrUnauthorized, ErrForbidden:
|
|
return e.Type == ErrorTypeAuth
|
|
case ErrTimeout:
|
|
return e.Type == ErrorTypeTimeout
|
|
case ErrConnectionFailed:
|
|
return e.Type == ErrorTypeConnection
|
|
}
|
|
|
|
// Check wrapped error
|
|
return errors.Is(e.Err, target)
|
|
}
|
|
|
|
// NewMonitorError creates a new MonitorError
|
|
func NewMonitorError(errorType ErrorType, op, instance string, err error) *MonitorError {
|
|
return &MonitorError{
|
|
Type: errorType,
|
|
Op: op,
|
|
Instance: instance,
|
|
Err: err,
|
|
Timestamp: time.Now(),
|
|
Retryable: isRetryable(errorType, err),
|
|
}
|
|
}
|
|
|
|
// WithNode adds node information to the error
|
|
func (e *MonitorError) WithNode(node string) *MonitorError {
|
|
e.Node = node
|
|
return e
|
|
}
|
|
|
|
// WithStatusCode adds HTTP status code to the error
|
|
func (e *MonitorError) WithStatusCode(code int) *MonitorError {
|
|
e.StatusCode = code
|
|
// Update retryable based on status code
|
|
if code >= 500 || code == 429 || code == 408 {
|
|
e.Retryable = true
|
|
} else if code >= 400 && code < 500 {
|
|
e.Retryable = false
|
|
}
|
|
return e
|
|
}
|
|
|
|
// isRetryable determines if an error should be retried
|
|
func isRetryable(errorType ErrorType, err error) bool {
|
|
switch errorType {
|
|
case ErrorTypeConnection, ErrorTypeTimeout:
|
|
return true
|
|
case ErrorTypeAuth, ErrorTypeValidation, ErrorTypeNotFound:
|
|
return false
|
|
case ErrorTypeInternal, ErrorTypeAPI:
|
|
// Check the underlying error
|
|
if err != nil {
|
|
return !errors.Is(err, ErrInvalidInput) && !errors.Is(err, ErrForbidden)
|
|
}
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
|
|
// Helper functions
|
|
|
|
// WrapConnectionError wraps a connection error with context
|
|
func WrapConnectionError(op, instance string, err error) error {
|
|
return NewMonitorError(ErrorTypeConnection, op, instance, err)
|
|
}
|
|
|
|
// WrapAuthError wraps an authentication error with context
|
|
func WrapAuthError(op, instance string, err error) error {
|
|
return NewMonitorError(ErrorTypeAuth, op, instance, err)
|
|
}
|
|
|
|
// WrapAPIError wraps an API error with context
|
|
func WrapAPIError(op, instance string, err error, statusCode int) error {
|
|
return NewMonitorError(ErrorTypeAPI, op, instance, err).WithStatusCode(statusCode)
|
|
}
|
|
|
|
// IsRetryableError checks if an error should be retried
|
|
func IsRetryableError(err error) bool {
|
|
var monErr *MonitorError
|
|
if errors.As(err, &monErr) {
|
|
return monErr.Retryable
|
|
}
|
|
|
|
// Check for wrapped standard errors
|
|
return errors.Is(err, ErrTimeout) || errors.Is(err, ErrConnectionFailed)
|
|
}
|
|
|
|
// IsAuthError checks if an error is an authentication error
|
|
func IsAuthError(err error) bool {
|
|
if err == nil {
|
|
return false
|
|
}
|
|
|
|
var monErr *MonitorError
|
|
if errors.As(err, &monErr) {
|
|
// Check type
|
|
if monErr.Type == ErrorTypeAuth {
|
|
return true
|
|
}
|
|
// Check status code for 401/403
|
|
if monErr.StatusCode == 401 || monErr.StatusCode == 403 {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Check for wrapped standard errors
|
|
if errors.Is(err, ErrUnauthorized) || errors.Is(err, ErrForbidden) {
|
|
return true
|
|
}
|
|
|
|
// Check error message for authentication indicators
|
|
errMsg := err.Error()
|
|
return strings.Contains(errMsg, "authentication error") ||
|
|
strings.Contains(errMsg, "authentication failed") ||
|
|
strings.Contains(errMsg, "401") ||
|
|
strings.Contains(errMsg, "403") ||
|
|
strings.Contains(errMsg, "unauthorized") ||
|
|
strings.Contains(errMsg, "forbidden")
|
|
} |