aboutsummaryrefslogtreecommitdiff
path: root/internal/monitor/monitor.go
blob: 5ec283a2a3fae2211de8a984a7f43bf7cf363612 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
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 {
	// Name returns the monitor's name
	Name() string

	// Type returns the monitor type (http, https, tcp, gemini, icmp, dns, graphql)
	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)
	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
}