Files
uptimemonitor/handler/monitor_handler.go
2025-08-03 12:17:15 +02:00

407 lines
10 KiB
Go

package handler
import (
"fmt"
"html/template"
"net/http"
"strconv"
"uptimemonitor"
"uptimemonitor/form"
"uptimemonitor/html"
"uptimemonitor/pkg/version"
)
func (h *Handler) ListMonitors() http.HandlerFunc {
tmpl := template.Must(template.ParseFS(html.FS, "monitor.html"))
type data struct {
Monitors []uptimemonitor.Monitor
Skeletons []int
}
return func(w http.ResponseWriter, r *http.Request) {
monitors, err := h.Store.ListMonitors(r.Context())
if err != nil {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
tmpl.ExecuteTemplate(w, "monitor_list", data{
Monitors: monitors,
Skeletons: make([]int, 60),
})
}
}
func (h *Handler) CreateMonitorPage() http.HandlerFunc {
tmpl := template.Must(template.ParseFS(html.FS, "layout.html", "app.html", "new.html"))
type data struct {
Version string
Form form.MonitorForm
User uptimemonitor.User
}
return func(w http.ResponseWriter, r *http.Request) {
tmpl.Execute(w, data{
Version: version.Version,
Form: form.MonitorForm{
HttpHeaders: `{
"Content-Type": "application/json"
}`,
HttpBody: `{}`,
WebhookHeaders: `{
"Content-Type": "application/json"
}`,
WebhookBody: `{
"url": "{{ .Url }}",
"code": "{{ .StatusCode }}"
}`,
},
User: getUserFromRequest(r),
})
}
}
func (h *Handler) CreateMonitorForm() http.HandlerFunc {
tmpl := template.Must(template.ParseFS(html.FS, "new.html"))
type data struct {
Form form.MonitorForm
}
return func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
f := form.MonitorForm{
HttpMethod: r.PostFormValue("http_method"),
Url: r.PostFormValue("url"),
HasCustomHeaders: r.PostFormValue("has_custom_headers") == "on",
HasCustomBody: r.PostFormValue("has_custom_body") == "on",
HttpHeaders: r.PostFormValue("http_headers"),
HttpBody: r.PostFormValue("http_body"),
HasWebhook: r.PostFormValue("has_webhook") == "on",
WebhookMethod: r.PostFormValue("webhook_method"),
WebhookUrl: r.PostFormValue("webhook_url"),
WebhookHeaders: r.PostFormValue("webhook_headers"),
WebhookBody: r.PostFormValue("webhook_body"),
}
if !f.Validate() {
w.WriteHeader(http.StatusBadRequest)
tmpl.ExecuteTemplate(w, "new_form", data{Form: f})
return
}
monitor := uptimemonitor.Monitor{
HttpMethod: f.HttpMethod,
Url: f.Url,
}
if f.HasCustomHeaders {
monitor.HttpHeaders = f.HttpHeaders
}
if f.HasCustomBody {
monitor.HttpBody = f.HttpBody
}
if f.HasWebhook {
monitor.WebhookUrl = f.WebhookUrl
monitor.WebhookMethod = f.WebhookMethod
monitor.WebhookHeaders = f.WebhookHeaders
monitor.WebhookBody = f.WebhookBody
}
m, err := h.Store.CreateMonitor(r.Context(), monitor)
if err != nil {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
w.Header().Set("HX-Redirect", m.URI())
}
}
func (h *Handler) MonitorPage() http.HandlerFunc {
tmpl := template.Must(template.ParseFS(html.FS, "layout.html", "app.html", "monitor.html"))
type data struct {
Version string
Monitor uptimemonitor.Monitor
Skeletons []int
User uptimemonitor.User
}
return func(w http.ResponseWriter, r *http.Request) {
m, err := h.Store.GetMonitorByUuid(r.Context(), r.PathValue("monitor"))
if err != nil || m.ID == 0 {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
tmpl.Execute(w, data{
Version: version.Version,
Monitor: m,
Skeletons: make([]int, 60),
User: getUserFromRequest(r),
})
}
}
func (h *Handler) MonitorStats() http.HandlerFunc {
tmpl := template.Must(template.ParseFS(html.FS, "monitor.html"))
type data struct {
ID int64
AvgResponseTime int64
Uptime string
Count int64
ChecksCount int64
FailureCount int64
}
return func(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(r.PathValue("monitor"))
if err != nil {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
m, err := h.Store.GetMonitorByID(r.Context(), id)
if err != nil {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
tmpl.ExecuteTemplate(w, "monitor_stats", data{
ID: int64(id),
AvgResponseTime: int64(m.AvgResponseTimeMs),
Uptime: fmt.Sprintf("%.1f", m.Uptime),
Count: m.IncidentsCount,
ChecksCount: m.N,
FailureCount: m.IncidentsCount,
})
}
}
func (h *Handler) ListMonitorIncidents() http.HandlerFunc {
tmpl := template.Must(template.ParseFS(html.FS, "monitor.html"))
type data struct {
ID int64
Monitor uptimemonitor.Monitor
Incidents []uptimemonitor.Incident
}
return func(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(r.PathValue("monitor"))
if err != nil {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
m, err := h.Store.GetMonitorByID(r.Context(), id)
if err != nil {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
incidents, err := h.Store.ListMonitorIncidents(r.Context(), int64(id))
if err != nil {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
tmpl.ExecuteTemplate(w, "monitor_incident_list", data{
ID: int64(id),
Monitor: m,
Incidents: incidents,
})
}
}
func (h *Handler) EditMonitorPage() http.HandlerFunc {
tmpl := template.Must(template.ParseFS(html.FS, "layout.html", "app.html", "edit.html"))
type data struct {
Version string
Form form.MonitorForm
User uptimemonitor.User
Monitor uptimemonitor.Monitor
}
return func(w http.ResponseWriter, r *http.Request) {
m, err := h.Store.GetMonitorByUuid(r.Context(), r.PathValue("monitor"))
if err != nil {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
f := form.MonitorForm{
Url: m.Url,
HttpMethod: m.HttpMethod,
HttpHeaders: m.HttpHeaders,
HttpBody: m.HttpBody,
HasCustomHeaders: m.HttpHeaders != "",
HasCustomBody: m.HttpBody != "",
HasWebhook: m.WebhookUrl != "",
WebhookUrl: m.WebhookUrl,
WebhookMethod: m.WebhookMethod,
WebhookHeaders: m.WebhookHeaders,
WebhookBody: m.WebhookBody,
}
if !f.HasCustomBody {
f.HttpBody = "{}"
}
if !f.HasCustomHeaders {
f.HttpHeaders = "{}"
}
tmpl.Execute(w, data{
Version: version.Version,
Monitor: m,
Form: f,
User: getUserFromRequest(r),
})
}
}
func (h *Handler) EditMonitorForm() http.HandlerFunc {
tmpl := template.Must(template.ParseFS(html.FS, "edit.html"))
type data struct {
Form form.MonitorForm
User uptimemonitor.User
Monitor uptimemonitor.Monitor
}
return func(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(r.PathValue("monitor"))
if err != nil {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
monitor, err := h.Store.GetMonitorByID(r.Context(), id)
if err != nil {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
r.ParseForm()
customHeaders := r.PostFormValue("http_headers")
customBody := r.PostFormValue("http_body")
if r.PostFormValue("has_custom_headers") != "on" {
customHeaders = ""
}
if r.PostFormValue("has_custom_body") != "on" {
customBody = ""
}
f := form.MonitorForm{
HttpMethod: r.PostFormValue("http_method"),
Url: r.PostFormValue("url"),
HasCustomHeaders: r.PostFormValue("has_custom_headers") == "on",
HasCustomBody: r.PostFormValue("has_custom_body") == "on",
HttpHeaders: customHeaders,
HttpBody: customBody,
HasWebhook: r.PostFormValue("has_webhook") == "on",
WebhookMethod: r.PostFormValue("webhook_method"),
WebhookUrl: r.PostFormValue("webhook_url"),
WebhookHeaders: r.PostFormValue("webhook_headers"),
WebhookBody: r.PostFormValue("webhook_body"),
}
if !f.Validate() {
w.WriteHeader(http.StatusBadRequest)
tmpl.ExecuteTemplate(w, "edit_form", data{
Monitor: monitor,
Form: f,
User: getUserFromRequest(r),
})
return
}
monitor.Url = f.Url
monitor.HttpMethod = f.HttpMethod
if f.HasCustomHeaders {
monitor.HttpHeaders = f.HttpHeaders
} else {
monitor.HttpHeaders = ""
}
if f.HasCustomBody {
monitor.HttpBody = f.HttpBody
} else {
monitor.HttpBody = ""
}
if f.HasWebhook {
monitor.WebhookUrl = f.WebhookUrl
monitor.WebhookMethod = f.WebhookMethod
monitor.WebhookHeaders = f.WebhookHeaders
monitor.WebhookBody = f.WebhookBody
} else {
monitor.WebhookUrl = ""
monitor.WebhookMethod = ""
monitor.WebhookHeaders = ""
monitor.WebhookBody = ""
}
err = h.Store.UpdateMonitor(r.Context(), monitor)
if err != nil {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
w.Header().Set("HX-Redirect", monitor.URI())
}
}
func (h *Handler) DeleteMonitorPage() http.HandlerFunc {
tmpl := template.Must(template.ParseFS(html.FS, "layout.html", "app.html", "delete.html"))
type data struct {
Version string
User uptimemonitor.User
Monitor uptimemonitor.Monitor
}
return func(w http.ResponseWriter, r *http.Request) {
uuid := r.PathValue("monitor")
m, err := h.Store.GetMonitorByUuid(r.Context(), uuid)
if err != nil || m.ID == 0 {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
tmpl.Execute(w, data{
Version: version.Version,
User: getUserFromRequest(r),
Monitor: m,
})
}
}
func (h *Handler) DeleteMonitorForm() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
id, _ := strconv.Atoi(r.PathValue("monitor"))
m, err := h.Store.GetMonitorByID(r.Context(), id)
if err != nil || m.ID == 0 {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
h.Store.DeleteMonitor(r.Context(), int64(id))
w.Header().Add("HX-Redirect", "/")
}
}