package monitor import ( "context" "fmt" "net" "time" "github.com/Fuwn/kaze/internal/config" ) // TCPMonitor monitors TCP endpoints type TCPMonitor struct { id string name string group string target string interval time.Duration timeout time.Duration retries int roundResponseTime bool roundUptime bool } // NewTCPMonitor creates a new TCP monitor func NewTCPMonitor(cfg config.MonitorConfig) (*TCPMonitor, error) { // Validate target format (should be host:port) _, _, err := net.SplitHostPort(cfg.Target) if err != nil { return nil, fmt.Errorf("invalid TCP target %q: must be host:port format: %w", cfg.Target, err) } return &TCPMonitor{ id: cfg.ID(), name: cfg.Name, group: cfg.Group, target: cfg.Target, interval: cfg.Interval.Duration, timeout: cfg.Timeout.Duration, retries: cfg.Retries, roundResponseTime: cfg.RoundResponseTime, roundUptime: cfg.RoundUptime, }, nil } // ID returns the unique identifier for this monitor func (m *TCPMonitor) ID() string { return m.id } // Name returns the monitor's name func (m *TCPMonitor) Name() string { return m.name } // Group returns the group this monitor belongs to func (m *TCPMonitor) Group() string { return m.group } // Type returns the monitor type func (m *TCPMonitor) Type() string { return "tcp" } // Target returns the monitor target func (m *TCPMonitor) Target() string { return m.target } // Interval returns the check interval func (m *TCPMonitor) Interval() time.Duration { return m.interval } // Retries returns the number of retry attempts func (m *TCPMonitor) Retries() int { return m.retries } // HideSSLDays returns whether to hide SSL days from display func (m *TCPMonitor) HideSSLDays() bool { return false // TCP doesn't use SSL } // RoundResponseTime returns whether to round response time func (m *TCPMonitor) RoundResponseTime() bool { return m.roundResponseTime } // RoundUptime returns whether to round uptime percentage func (m *TCPMonitor) RoundUptime() bool { return m.roundUptime } // Check performs the TCP connection check func (m *TCPMonitor) Check(ctx context.Context) *Result { result := &Result{ MonitorName: m.id, Timestamp: time.Now(), } // Create a dialer with timeout dialer := &net.Dialer{ Timeout: m.timeout, } // Attempt to connect start := time.Now() conn, err := dialer.DialContext(ctx, "tcp", m.target) result.ResponseTime = time.Since(start) if err != nil { result.Status = StatusDown result.Error = fmt.Errorf("connection failed: %w", err) return result } defer conn.Close() result.Status = StatusUp return result }