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", "/") } }