Refactor monitor: allow insecure requests & use of simple icons

This commit is contained in:
Svilen Markov 2024-05-30 22:10:12 +01:00
parent b63b5eb262
commit 21fd842bde
4 changed files with 60 additions and 34 deletions

View File

@ -22,13 +22,13 @@
{{ define "site" }} {{ define "site" }}
{{ if .IconUrl }} {{ if .IconUrl }}
<img class="monitor-site-icon" src="{{ .IconUrl }}" alt="" loading="lazy"> <img class="monitor-site-icon{{ if .IsSimpleIcon }} simple-icon{{ end }}" src="{{ .IconUrl }}" alt="" loading="lazy">
{{ end }} {{ end }}
<div> <div>
<a class="size-h3 color-highlight" href="{{ .Url }}" {{ if not .SameTab }}target="_blank"{{ end }} rel="noreferrer">{{ .Title }}</a> <a class="size-h3 color-highlight" href="{{ .URL }}" {{ if not .SameTab }}target="_blank"{{ end }} rel="noreferrer">{{ .Title }}</a>
<ul class="list-horizontal-text"> <ul class="list-horizontal-text">
{{ if not .Status.Error }} {{ if not .Status.Error }}
<li>{{ .StatusText }}</li> <li title="{{ .Status.Code }}">{{ .StatusText }}</li>
<li>{{ .Status.ResponseTime.Milliseconds | formatNumber }}ms</li> <li>{{ .Status.ResponseTime.Milliseconds | formatNumber }}ms</li>
{{ else if .Status.TimedOut }} {{ else if .Status.TimedOut }}
<li class="color-negative">Timed Out</li> <li class="color-negative">Timed Out</li>
@ -37,7 +37,7 @@
{{ end }} {{ end }}
</ul> </ul>
</div> </div>
{{ if eq .StatusStyle "good" }} {{ if eq .StatusStyle "ok" }}
<div class="monitor-site-status-icon"> <div class="monitor-site-status-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="var(--color-positive)"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="var(--color-positive)">
<path fill-rule="evenodd" d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm13.36-1.814a.75.75 0 1 0-1.22-.872l-3.236 4.53L9.53 12.22a.75.75 0 0 0-1.06 1.06l2.25 2.25a.75.75 0 0 0 1.14-.094l3.75-5.25Z" clip-rule="evenodd" /> <path fill-rule="evenodd" d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm13.36-1.814a.75.75 0 1 0-1.22-.872l-3.236 4.53L9.53 12.22a.75.75 0 0 0-1.06 1.06l2.25 2.25a.75.75 0 0 0 1.14-.094l3.75-5.25Z" clip-rule="evenodd" />

View File

@ -7,6 +7,11 @@ import (
"time" "time"
) )
type SiteStatusRequest struct {
URL string `yaml:"url"`
AllowInsecure bool `yaml:"allow-insecure"`
}
type SiteStatus struct { type SiteStatus struct {
Code int Code int
TimedOut bool TimedOut bool
@ -14,14 +19,28 @@ type SiteStatus struct {
Error error Error error
} }
func getSiteStatusTask(request *http.Request) (SiteStatus, error) { func getSiteStatusTask(statusRequest *SiteStatusRequest) (SiteStatus, error) {
request, err := http.NewRequest(http.MethodGet, statusRequest.URL, nil)
if err != nil {
return SiteStatus{
Error: err,
}, nil
}
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3) ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
defer cancel() defer cancel()
request = request.WithContext(ctx) request = request.WithContext(ctx)
start := time.Now() requestSentAt := time.Now()
response, err := http.DefaultClient.Do(request) var response *http.Response
took := time.Since(start)
status := SiteStatus{ResponseTime: took} if !statusRequest.AllowInsecure {
response, err = defaultClient.Do(request)
} else {
response, err = defaultInsecureClient.Do(request)
}
status := SiteStatus{ResponseTime: time.Since(requestSentAt)}
if err != nil { if err != nil {
if errors.Is(err, context.DeadlineExceeded) { if errors.Is(err, context.DeadlineExceeded) {
@ -29,7 +48,7 @@ func getSiteStatusTask(request *http.Request) (SiteStatus, error) {
} }
status.Error = err status.Error = err
return status, err return status, nil
} }
defer response.Body.Close() defer response.Body.Close()
@ -39,7 +58,7 @@ func getSiteStatusTask(request *http.Request) (SiteStatus, error) {
return status, nil return status, nil
} }
func FetchStatusesForRequests(requests []*http.Request) ([]SiteStatus, error) { func FetchStatusForSites(requests []*SiteStatusRequest) ([]SiteStatus, error) {
job := newJob(getSiteStatusTask, requests).withWorkers(20) job := newJob(getSiteStatusTask, requests).withWorkers(20)
results, _, err := workerPoolDo(job) results, _, err := workerPoolDo(job)

View File

@ -2,6 +2,7 @@ package feed
import ( import (
"context" "context"
"crypto/tls"
"encoding/json" "encoding/json"
"encoding/xml" "encoding/xml"
"fmt" "fmt"
@ -11,8 +12,19 @@ import (
"time" "time"
) )
const defaultClientTimeout = 5 * time.Second
var defaultClient = &http.Client{ var defaultClient = &http.Client{
Timeout: 5 * time.Second, Timeout: defaultClientTimeout,
}
var insecureClientTransport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
var defaultInsecureClient = &http.Client{
Timeout: defaultClientTimeout,
Transport: insecureClientTransport,
} }
type RequestDoer interface { type RequestDoer interface {

View File

@ -2,9 +2,7 @@ package widget
import ( import (
"context" "context"
"fmt"
"html/template" "html/template"
"net/http"
"strconv" "strconv"
"time" "time"
@ -37,20 +35,21 @@ func statusCodeToText(status int) string {
func statusCodeToStyle(status int) string { func statusCodeToStyle(status int) string {
if status == 200 { if status == 200 {
return "good" return "ok"
} }
return "bad" return "error"
} }
type Monitor struct { type Monitor struct {
widgetBase `yaml:",inline"` widgetBase `yaml:",inline"`
Sites []struct { Sites []struct {
Title string `yaml:"title"` *feed.SiteStatusRequest `yaml:",inline"`
Url OptionalEnvString `yaml:"url"`
IconUrl string `yaml:"icon"`
SameTab bool `yaml:"same-tab"`
Status *feed.SiteStatus `yaml:"-"` Status *feed.SiteStatus `yaml:"-"`
Title string `yaml:"title"`
IconUrl string `yaml:"icon"`
IsSimpleIcon bool `yaml:"-"`
SameTab bool `yaml:"same-tab"`
StatusText string `yaml:"-"` StatusText string `yaml:"-"`
StatusStyle string `yaml:"-"` StatusStyle string `yaml:"-"`
} `yaml:"sites"` } `yaml:"sites"`
@ -60,25 +59,21 @@ type Monitor struct {
func (widget *Monitor) Initialize() error { func (widget *Monitor) Initialize() error {
widget.withTitle("Monitor").withCacheDuration(5 * time.Minute) widget.withTitle("Monitor").withCacheDuration(5 * time.Minute)
for i := range widget.Sites {
widget.Sites[i].IconUrl, widget.Sites[i].IsSimpleIcon = toSimpleIconIfPrefixed(widget.Sites[i].IconUrl)
}
return nil return nil
} }
func (widget *Monitor) Update(ctx context.Context) { func (widget *Monitor) Update(ctx context.Context) {
requests := make([]*http.Request, len(widget.Sites)) requests := make([]*feed.SiteStatusRequest, len(widget.Sites))
for i := range widget.Sites { for i := range widget.Sites {
request, err := http.NewRequest("GET", string(widget.Sites[i].Url), nil) requests[i] = widget.Sites[i].SiteStatusRequest
if err != nil {
message := fmt.Errorf("failed to create http request for %s: %s", widget.Sites[i].Url, err)
widget.withNotice(message)
continue
} }
requests[i] = request statuses, err := feed.FetchStatusForSites(requests)
}
statuses, err := feed.FetchStatusesForRequests(requests)
if !widget.canContinueUpdateAfterHandlingErr(err) { if !widget.canContinueUpdateAfterHandlingErr(err) {
return return