package monitor import ( "context" "time" "github.com/Fuwn/kaze/internal/config" "github.com/Fuwn/kaze/internal/storage" ) // Result represents the outcome of a monitor check type Result struct { MonitorName string Timestamp time.Time Status Status ResponseTime time.Duration StatusCode int // HTTP status code (0 for non-HTTP) Error error SSLExpiry *time.Time SSLDaysLeft int } // Status represents the status of a monitor type Status string const ( StatusUp Status = "up" StatusDown Status = "down" StatusDegraded Status = "degraded" ) // Monitor is the interface that all monitor types must implement type Monitor interface { // ID returns the unique identifier for this monitor (group/name format) ID() string // Name returns the monitor's name Name() string // Group returns the group this monitor belongs to Group() string // Type returns the monitor type (http, https, tcp, gemini, icmp, dns, graphql, database) Type() string // Target returns the monitor target (URL or host:port) Target() string // Interval returns the check interval Interval() time.Duration // Retries returns the number of retry attempts Retries() int // HideSSLDays returns whether to hide SSL days from display HideSSLDays() bool // RoundResponseTime returns whether to round response time RoundResponseTime() bool // RoundUptime returns whether to round uptime percentage RoundUptime() bool // Check performs the monitoring check and returns the result Check(ctx context.Context) *Result } // New creates a new monitor based on the configuration func New(cfg config.MonitorConfig) (Monitor, error) { switch cfg.Type { case "http", "https": return NewHTTPMonitor(cfg) case "tcp": return NewTCPMonitor(cfg) case "gemini": return NewGeminiMonitor(cfg) case "icmp": return NewICMPMonitor(cfg) case "dns": return NewDNSMonitor(cfg) case "graphql": return NewGraphQLMonitor(cfg) case "database", "db": return NewDatabaseMonitor(cfg) default: return nil, &UnsupportedTypeError{Type: cfg.Type} } } // UnsupportedTypeError is returned when an unknown monitor type is specified type UnsupportedTypeError struct { Type string } func (e *UnsupportedTypeError) Error() string { return "unsupported monitor type: " + e.Type } // ToCheckResult converts a monitor Result to a storage CheckResult func (r *Result) ToCheckResult() *storage.CheckResult { cr := &storage.CheckResult{ MonitorName: r.MonitorName, Timestamp: r.Timestamp, Status: string(r.Status), ResponseTime: r.ResponseTime.Milliseconds(), StatusCode: r.StatusCode, SSLExpiry: r.SSLExpiry, SSLDaysLeft: r.SSLDaysLeft, } if r.Error != nil { cr.Error = r.Error.Error() } return cr } // ToCheckResultWithOptions converts a monitor Result to a storage CheckResult with display options applied func (r *Result) ToCheckResultWithOptions(mon Monitor) *storage.CheckResult { cr := r.ToCheckResult() // Apply rounding if enabled if mon.RoundResponseTime() { // Round to nearest second (1000ms) cr.ResponseTime = ((cr.ResponseTime + 500) / 1000) * 1000 } // Hide SSL days if enabled if mon.HideSSLDays() { cr.SSLDaysLeft = 0 cr.SSLExpiry = nil } return cr }