aboutsummaryrefslogtreecommitdiff
path: root/internal/monitor/monitor.go
blob: d530e068455090609a27f63f7b98724d7d43b73e (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
126
127
128
129
130
131
132
133
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
}