From 5006e04219e1c71054fb7dd53296cfd825831154 Mon Sep 17 00:00:00 2001 From: Michael Freeman Date: Wed, 26 Feb 2025 13:30:16 -0600 Subject: [PATCH] cleanup --- pkg/cloud/api/server.go | 50 +------------- pkg/cloud/api/types.go | 60 +++++++++++++++++ pkg/config/config.go | 91 ------------------------- pkg/config/interfaces.go | 6 ++ pkg/config/types.go | 92 ++++++++++++++++++++++++++ pkg/http/middleware.go | 28 +------- serviceradar-next/src/app/favicon.ico | Bin 0 -> 15406 bytes 7 files changed, 160 insertions(+), 167 deletions(-) create mode 100644 pkg/cloud/api/types.go create mode 100644 pkg/config/interfaces.go create mode 100644 pkg/config/types.go create mode 100644 serviceradar-next/src/app/favicon.ico diff --git a/pkg/cloud/api/server.go b/pkg/cloud/api/server.go index 2dadfa5..64cf7ac 100644 --- a/pkg/cloud/api/server.go +++ b/pkg/cloud/api/server.go @@ -6,64 +6,15 @@ import ( "encoding/json" "log" "net/http" - "sync" "time" "github.com/carverauto/serviceradar/pkg/checker/snmp" "github.com/carverauto/serviceradar/pkg/db" srHttp "github.com/carverauto/serviceradar/pkg/http" "github.com/carverauto/serviceradar/pkg/metrics" - "github.com/carverauto/serviceradar/pkg/models" "github.com/gorilla/mux" ) -type ServiceStatus struct { - Name string `json:"name"` - Available bool `json:"available"` - Message string `json:"message"` - Type string `json:"type"` // e.g., "process", "port", "blockchain", etc. - Details json.RawMessage `json:"details"` // Flexible field for service-specific data -} - -type NodeStatus struct { - NodeID string `json:"node_id"` - IsHealthy bool `json:"is_healthy"` - LastUpdate time.Time `json:"last_update"` - Services []ServiceStatus `json:"services"` - UpTime string `json:"uptime"` - FirstSeen time.Time `json:"first_seen"` - Metrics []models.MetricPoint `json:"metrics,omitempty"` -} - -type SystemStatus struct { - TotalNodes int `json:"total_nodes"` - HealthyNodes int `json:"healthy_nodes"` - LastUpdate time.Time `json:"last_update"` -} - -type NodeHistory struct { - NodeID string - Timestamp time.Time - IsHealthy bool - Services []ServiceStatus -} - -type NodeHistoryPoint struct { - Timestamp time.Time `json:"timestamp"` - IsHealthy bool `json:"is_healthy"` -} - -type APIServer struct { - mu sync.RWMutex - nodes map[string]*NodeStatus - router *mux.Router - nodeHistoryHandler func(nodeID string) ([]NodeHistoryPoint, error) - metricsManager metrics.MetricCollector - snmpManager snmp.SNMPManager - db db.Service - knownPollers []string -} - func NewAPIServer(options ...func(server *APIServer)) *APIServer { s := &APIServer{ nodes: make(map[string]*NodeStatus), @@ -273,6 +224,7 @@ func (s *APIServer) getNodes(w http.ResponseWriter, _ *http.Request) { for _, known := range s.knownPollers { if id == known { nodes = append(nodes, node) + break } } diff --git a/pkg/cloud/api/types.go b/pkg/cloud/api/types.go new file mode 100644 index 0000000..cb04b9f --- /dev/null +++ b/pkg/cloud/api/types.go @@ -0,0 +1,60 @@ +package api + +import ( + "encoding/json" + "sync" + "time" + + "github.com/carverauto/serviceradar/pkg/checker/snmp" + "github.com/carverauto/serviceradar/pkg/db" + "github.com/carverauto/serviceradar/pkg/metrics" + "github.com/carverauto/serviceradar/pkg/models" + "github.com/gorilla/mux" +) + +type ServiceStatus struct { + Name string `json:"name"` + Available bool `json:"available"` + Message string `json:"message"` + Type string `json:"type"` // e.g., "process", "port", "blockchain", etc. + Details json.RawMessage `json:"details"` // Flexible field for service-specific data +} + +type NodeStatus struct { + NodeID string `json:"node_id"` + IsHealthy bool `json:"is_healthy"` + LastUpdate time.Time `json:"last_update"` + Services []ServiceStatus `json:"services"` + UpTime string `json:"uptime"` + FirstSeen time.Time `json:"first_seen"` + Metrics []models.MetricPoint `json:"metrics,omitempty"` +} + +type SystemStatus struct { + TotalNodes int `json:"total_nodes"` + HealthyNodes int `json:"healthy_nodes"` + LastUpdate time.Time `json:"last_update"` +} + +type NodeHistory struct { + NodeID string + Timestamp time.Time + IsHealthy bool + Services []ServiceStatus +} + +type NodeHistoryPoint struct { + Timestamp time.Time `json:"timestamp"` + IsHealthy bool `json:"is_healthy"` +} + +type APIServer struct { + mu sync.RWMutex + nodes map[string]*NodeStatus + router *mux.Router + nodeHistoryHandler func(nodeID string) ([]NodeHistoryPoint, error) + metricsManager metrics.MetricCollector + snmpManager snmp.SNMPManager + db db.Service + knownPollers []string +} diff --git a/pkg/config/config.go b/pkg/config/config.go index 6d98883..4bdf094 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -5,98 +5,12 @@ import ( "encoding/json" "fmt" "os" - "time" - - "github.com/carverauto/serviceradar/pkg/models" ) var ( errInvalidDuration = fmt.Errorf("invalid duration") ) -type Duration time.Duration - -func (d *Duration) UnmarshalJSON(b []byte) error { - var v interface{} - if err := json.Unmarshal(b, &v); err != nil { - return err - } - - switch value := v.(type) { - case float64: - // parse numeric as nanoseconds - *d = Duration(time.Duration(value)) - return nil - case string: - dur, err := time.ParseDuration(value) - if err != nil { - return fmt.Errorf("invalid duration: %w", err) - } - - *d = Duration(dur) - - return nil - default: - return errInvalidDuration - } -} - -// AgentConfig represents the configuration for an agent instance. -type AgentConfig struct { - CheckersDir string `json:"checkers_dir"` // e.g., /etc/serviceradar/checkers - ListenAddr string `json:"listen_addr"` // e.g., :50051 - ServiceName string `json:"service_name"` // e.g., "agent" - Security *models.SecurityConfig `json:"security"` -} - -// Check represents a generic service check configuration. -type Check struct { - ServiceType string `json:"service_type"` // e.g., "grpc", "process", "port" - ServiceName string `json:"service_name"` - Details string `json:"details,omitempty"` // Service-specific details - Port int32 `json:"port,omitempty"` // For port checkers - Config json.RawMessage `json:"config,omitempty"` // Checker-specific configuration -} - -// AgentDefinition represents a remote agent and its checks. -type AgentDefinition struct { - Address string `json:"address"` // gRPC address of the agent - Checks []Check `json:"checks"` // List of checks to run on this agent -} - -// PollerConfig represents the configuration for a poller instance. -type PollerConfig struct { - Agents map[string]AgentDefinition `json:"agents"` // Map of agent ID to agent definition - CloudAddress string `json:"cloud_address"` // Address of cloud service - PollInterval Duration `json:"poll_interval"` // How often to poll agents - PollerID string `json:"poller_id"` // Unique identifier for this poller -} - -// WebhookConfig represents a webhook notification configuration. -type WebhookConfig struct { - Enabled bool `json:"enabled"` - URL string `json:"url"` - Cooldown Duration `json:"cooldown"` - Template string `json:"template"` - Headers []Header `json:"headers,omitempty"` // Optional custom headers -} - -// Header represents a custom HTTP header. -type Header struct { - Key string `json:"key"` - Value string `json:"value"` -} - -// CloudConfig represents the configuration for the cloud service. -type CloudConfig struct { - ListenAddr string `json:"listen_addr"` - GrpcAddr string `json:"grpc_addr,omitempty"` - DBPath string `json:"db_path"` - AlertThreshold Duration `json:"alert_threshold"` - KnownPollers []string `json:"known_pollers"` - Webhooks []WebhookConfig `json:"webhooks,omitempty"` -} - // LoadFile is a generic helper that loads a JSON file from path into // the struct pointed to by dst. func LoadFile(path string, dst interface{}) error { @@ -112,11 +26,6 @@ func LoadFile(path string, dst interface{}) error { return nil } -// Validator interface for configurations that need validation. -type Validator interface { - Validate() error -} - // ValidateConfig validates a configuration if it implements Validator. func ValidateConfig(cfg interface{}) error { if v, ok := cfg.(Validator); ok { diff --git a/pkg/config/interfaces.go b/pkg/config/interfaces.go new file mode 100644 index 0000000..c6da3dd --- /dev/null +++ b/pkg/config/interfaces.go @@ -0,0 +1,6 @@ +package config + +// Validator interface for configurations that need validation. +type Validator interface { + Validate() error +} diff --git a/pkg/config/types.go b/pkg/config/types.go new file mode 100644 index 0000000..5807c35 --- /dev/null +++ b/pkg/config/types.go @@ -0,0 +1,92 @@ +package config + +import ( + "encoding/json" + "fmt" + "time" + + "github.com/carverauto/serviceradar/pkg/models" +) + +type Duration time.Duration + +func (d *Duration) UnmarshalJSON(b []byte) error { + var v interface{} + if err := json.Unmarshal(b, &v); err != nil { + return err + } + + switch value := v.(type) { + case float64: + // parse numeric as nanoseconds + *d = Duration(time.Duration(value)) + return nil + case string: + dur, err := time.ParseDuration(value) + if err != nil { + return fmt.Errorf("invalid duration: %w", err) + } + + *d = Duration(dur) + + return nil + default: + return errInvalidDuration + } +} + +// AgentConfig represents the configuration for an agent instance. +type AgentConfig struct { + CheckersDir string `json:"checkers_dir"` // e.g., /etc/serviceradar/checkers + ListenAddr string `json:"listen_addr"` // e.g., :50051 + ServiceName string `json:"service_name"` // e.g., "agent" + Security *models.SecurityConfig `json:"security"` +} + +// Check represents a generic service check configuration. +type Check struct { + ServiceType string `json:"service_type"` // e.g., "grpc", "process", "port" + ServiceName string `json:"service_name"` + Details string `json:"details,omitempty"` // Service-specific details + Port int32 `json:"port,omitempty"` // For port checkers + Config json.RawMessage `json:"config,omitempty"` // Checker-specific configuration +} + +// AgentDefinition represents a remote agent and its checks. +type AgentDefinition struct { + Address string `json:"address"` // gRPC address of the agent + Checks []Check `json:"checks"` // List of checks to run on this agent +} + +// PollerConfig represents the configuration for a poller instance. +type PollerConfig struct { + Agents map[string]AgentDefinition `json:"agents"` // Map of agent ID to agent definition + CloudAddress string `json:"cloud_address"` // Address of cloud service + PollInterval Duration `json:"poll_interval"` // How often to poll agents + PollerID string `json:"poller_id"` // Unique identifier for this poller +} + +// WebhookConfig represents a webhook notification configuration. +type WebhookConfig struct { + Enabled bool `json:"enabled"` + URL string `json:"url"` + Cooldown Duration `json:"cooldown"` + Template string `json:"template"` + Headers []Header `json:"headers,omitempty"` // Optional custom headers +} + +// Header represents a custom HTTP header. +type Header struct { + Key string `json:"key"` + Value string `json:"value"` +} + +// CloudConfig represents the configuration for the cloud service. +type CloudConfig struct { + ListenAddr string `json:"listen_addr"` + GrpcAddr string `json:"grpc_addr,omitempty"` + DBPath string `json:"db_path"` + AlertThreshold Duration `json:"alert_threshold"` + KnownPollers []string `json:"known_pollers"` + Webhooks []WebhookConfig `json:"webhooks,omitempty"` +} diff --git a/pkg/http/middleware.go b/pkg/http/middleware.go index eaaa36d..32b6aa0 100644 --- a/pkg/http/middleware.go +++ b/pkg/http/middleware.go @@ -5,7 +5,6 @@ import ( "log" "net/http" "os" - "strings" ) // CommonMiddleware returns an http.Handler that sets up typical @@ -19,13 +18,10 @@ func CommonMiddleware(next http.Handler) http.Handler { if r.Method == http.MethodOptions { // Preflight request response w.WriteHeader(http.StatusOK) + return } - // You might also add a request logging line: - // TODO: should log for debug only - // log.Printf("[HTTP] %s %s", r.Method, r.URL.Path) - next.ServeHTTP(w, r) }) } @@ -38,13 +34,6 @@ func APIKeyMiddleware(next http.Handler) http.Handler { } return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // Skip API key check for static file requests - if isStaticFileRequest(r.URL.Path) { - next.ServeHTTP(w, r) - - return - } - // Skip API key check if it's not configured (development mode) if apiKey == "" { next.ServeHTTP(w, r) @@ -69,18 +58,3 @@ func APIKeyMiddleware(next http.Handler) http.Handler { next.ServeHTTP(w, r) }) } - -// isStaticFileRequest returns true if the request is for static content. -func isStaticFileRequest(path string) bool { - // Skip API key check for static files (adjust as needed) - staticExtensions := []string{".js", ".css", ".html", ".png", ".jpg", ".svg", ".ico", ".woff", ".woff2"} - - for _, ext := range staticExtensions { - if strings.HasSuffix(path, ext) { - return true - } - } - - // Also skip for the root path (which serves index.html) - return path == "/" -} diff --git a/serviceradar-next/src/app/favicon.ico b/serviceradar-next/src/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..dba6d34c72fafb82c254c7bb48358b2695d96245 GIT binary patch literal 15406 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 RC*1clM;OQdoBu~<;J=DbYNY@G literal 0 HcmV?d00001