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
}
|