aboutsummaryrefslogtreecommitdiff
path: root/backend/internal/api/handler.go
diff options
context:
space:
mode:
authorzwlucas <lucas.fariamo08@gmail.com>2026-05-29 18:57:37 +0000
committerzwlucas <lucas.fariamo08@gmail.com>2026-05-29 18:57:37 +0000
commit6cc0bfd1d79074df790272b8091b1f0226d14283 (patch)
treecab18b0f708cf3b0eaeeeb98e2cf81459365b33e /backend/internal/api/handler.go
downloadyaum-6cc0bfd1d79074df790272b8091b1f0226d14283.tar.gz
yaum-6cc0bfd1d79074df790272b8091b1f0226d14283.zip
feat: upload project
Signed-off-by: zwlucas <lucas.fariamo08@gmail.com>
Diffstat (limited to 'backend/internal/api/handler.go')
-rw-r--r--backend/internal/api/handler.go243
1 files changed, 243 insertions, 0 deletions
diff --git a/backend/internal/api/handler.go b/backend/internal/api/handler.go
new file mode 100644
index 0000000..f304351
--- /dev/null
+++ b/backend/internal/api/handler.go
@@ -0,0 +1,243 @@
+package api
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "strconv"
+ "time"
+
+ "yaum/internal/monitor"
+ "yaum/internal/model"
+ "yaum/internal/store"
+)
+
+type ServiceResponse struct {
+ model.Service
+ LastHeartbeat *model.Heartbeat `json:"last_heartbeat,omitempty"`
+}
+
+type Handler struct {
+ store store.Store
+ timeout int
+}
+
+func NewHandler(s store.Store, timeoutSec int) *Handler {
+ return &Handler{store: s, timeout: timeoutSec}
+}
+
+func (h *Handler) ListServices(w http.ResponseWriter, r *http.Request) {
+ services, err := h.store.ListServices()
+ if err != nil {
+ writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
+ return
+ }
+ resp := make([]ServiceResponse, len(services))
+ for i, svc := range services {
+ resp[i] = ServiceResponse{
+ Service: svc.Service,
+ LastHeartbeat: svc.LastHeartbeat,
+ }
+ }
+ writeJSON(w, http.StatusOK, resp)
+}
+
+func (h *Handler) CreateService(w http.ResponseWriter, r *http.Request) {
+ var svc model.Service
+ if err := json.NewDecoder(r.Body).Decode(&svc); err != nil {
+ writeJSON(w, http.StatusBadRequest, map[string]string{"error": "json invalido: " + err.Error()})
+ return
+ }
+ if svc.Name == "" || svc.URL == "" {
+ writeJSON(w, http.StatusBadRequest, map[string]string{"error": "name e url sao obrigatorios"})
+ return
+ }
+ created, err := h.store.AddService(svc)
+ if err != nil {
+ writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
+ return
+ }
+ writeJSON(w, http.StatusCreated, created)
+}
+
+func (h *Handler) ToggleService(w http.ResponseWriter, r *http.Request) {
+ id, err := strconv.Atoi(r.PathValue("id"))
+ if err != nil {
+ writeJSON(w, http.StatusBadRequest, map[string]string{"error": "id invalido"})
+ return
+ }
+ svc, err := h.store.ToggleService(id)
+ if err != nil {
+ writeJSON(w, http.StatusNotFound, map[string]string{"error": err.Error()})
+ return
+ }
+ writeJSON(w, http.StatusOK, svc)
+}
+
+func (h *Handler) TestService(w http.ResponseWriter, r *http.Request) {
+ id, err := strconv.Atoi(r.PathValue("id"))
+ if err != nil {
+ writeJSON(w, http.StatusBadRequest, map[string]string{"error": "id invalido"})
+ return
+ }
+
+ svc, err := h.store.GetService(id)
+ if err != nil || svc == nil {
+ writeJSON(w, http.StatusNotFound, map[string]string{"error": "servico nao encontrado"})
+ return
+ }
+
+ ctx, cancel := context.WithTimeout(r.Context(), time.Duration(h.timeout)*time.Second)
+ defer cancel()
+
+ hb := monitor.CheckHTTP(ctx, svc.URL, svc.KeywordToFind)
+ hb.ServiceID = svc.ID
+
+ if saverr := h.store.AddHeartbeat(hb); saverr != nil {
+ writeJSON(w, http.StatusInternalServerError, map[string]string{"error": saverr.Error()})
+ return
+ }
+
+ writeJSON(w, http.StatusOK, hb)
+}
+
+func (h *Handler) UpdateService(w http.ResponseWriter, r *http.Request) {
+ id, err := strconv.Atoi(r.PathValue("id"))
+ if err != nil {
+ writeJSON(w, http.StatusBadRequest, map[string]string{"error": "id invalido"})
+ return
+ }
+
+ var svc model.Service
+ if err := json.NewDecoder(r.Body).Decode(&svc); err != nil {
+ writeJSON(w, http.StatusBadRequest, map[string]string{"error": "json invalido: " + err.Error()})
+ return
+ }
+
+ updated, err := h.store.UpdateService(id, svc)
+ if err != nil {
+ writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
+ return
+ }
+ writeJSON(w, http.StatusOK, updated)
+}
+
+func (h *Handler) DeleteService(w http.ResponseWriter, r *http.Request) {
+ id, err := strconv.Atoi(r.PathValue("id"))
+ if err != nil {
+ writeJSON(w, http.StatusBadRequest, map[string]string{"error": "id invalido"})
+ return
+ }
+ if err := h.store.DeleteService(id); err != nil {
+ writeJSON(w, http.StatusNotFound, map[string]string{"error": err.Error()})
+ return
+ }
+ w.WriteHeader(http.StatusNoContent)
+}
+
+func (h *Handler) GetHistory(w http.ResponseWriter, r *http.Request) {
+ id, err := strconv.Atoi(r.PathValue("id"))
+ if err != nil {
+ writeJSON(w, http.StatusBadRequest, map[string]string{"error": "id invalido"})
+ return
+ }
+ history, err := h.store.GetHistory(id, 50)
+ if err != nil {
+ writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
+ return
+ }
+ writeJSON(w, http.StatusOK, history)
+}
+
+func (h *Handler) GetActiveMaintenance(w http.ResponseWriter, r *http.Request) {
+ m, err := h.store.GetActiveMaintenance(time.Now())
+ if err != nil {
+ writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
+ return
+ }
+ if m == nil {
+ writeJSON(w, http.StatusOK, map[string]any{"maintenance": nil})
+ return
+ }
+ writeJSON(w, http.StatusOK, map[string]any{"maintenance": m})
+}
+
+func (h *Handler) BadgeSVG(w http.ResponseWriter, r *http.Request) {
+ id, err := strconv.Atoi(r.PathValue("id"))
+ if err != nil {
+ w.Header().Set("Content-Type", "image/svg+xml")
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte(badgeSVG("ERRO", "#888")))
+ return
+ }
+
+ svc, err := h.store.GetService(id)
+ if err != nil || svc == nil {
+ w.Header().Set("Content-Type", "image/svg+xml")
+ w.WriteHeader(http.StatusNotFound)
+ w.Write([]byte(badgeSVG("N/A", "#888")))
+ return
+ }
+
+ stats, _ := h.store.GetServiceStats(id)
+ lastHb, _ := h.store.GetLastHeartbeat(id)
+
+ var label, color string
+ if lastHb == nil {
+ label = "AGUARDANDO"
+ color = "#888"
+ } else if lastHb.IsUp {
+ if stats != nil && stats.Uptime30d > 0 {
+ label = "UP " + fmt.Sprintf("%.1f", stats.Uptime30d) + "%"
+ } else {
+ label = "UP"
+ }
+ color = "#22f06a"
+ } else {
+ label = "DOWN"
+ color = "#ff4060"
+ }
+
+ w.Header().Set("Content-Type", "image/svg+xml")
+ w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
+ w.Write([]byte(badgeSVG(label, color)))
+}
+
+func badgeSVG(label string, color string) string {
+ textWidth := len(label) * 8
+ if textWidth < 40 {
+ textWidth = 40
+ }
+ totalWidth := 20 + textWidth + 8
+
+ return fmt.Sprintf(
+ `<svg xmlns="http://www.w3.org/2000/svg" width="%d" height="20">
+ <rect width="%d" height="20" rx="3" fill="#333"/>
+ <rect x="%d" width="%d" height="20" rx="3" fill="%s"/>
+ <text x="%d" y="14" font-family="monospace, sans-serif" font-size="11" font-weight="bold" fill="#fff" text-anchor="middle">%s</text>
+</svg>`,
+ totalWidth, totalWidth, totalWidth-textWidth-8, textWidth+8, color,
+ 10+(textWidth/2), label,
+ )
+}
+
+func (h *Handler) GetStats(w http.ResponseWriter, r *http.Request) {
+ id, err := strconv.Atoi(r.PathValue("id"))
+ if err != nil {
+ writeJSON(w, http.StatusBadRequest, map[string]string{"error": "id invalido"})
+ return
+ }
+ stats, err := h.store.GetServiceStats(id)
+ if err != nil {
+ writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
+ return
+ }
+ writeJSON(w, http.StatusOK, stats)
+}
+
+func writeJSON(w http.ResponseWriter, status int, data any) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(status)
+ json.NewEncoder(w).Encode(data)
+}