197 lines
4.9 KiB
Go
197 lines
4.9 KiB
Go
// ©AngelaMos | 2026
|
|
// handler.go
|
|
|
|
package alerts
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/go-playground/validator/v10"
|
|
|
|
"github.com/carterperez-dev/monitor-the-situation/backend/internal/core"
|
|
"github.com/carterperez-dev/monitor-the-situation/backend/internal/middleware"
|
|
)
|
|
|
|
type Handler struct {
|
|
repo Repository
|
|
validator *validator.Validate
|
|
}
|
|
|
|
func NewHandler(repo Repository) *Handler {
|
|
return &Handler{
|
|
repo: repo,
|
|
validator: validator.New(validator.WithRequiredStructEnabled()),
|
|
}
|
|
}
|
|
|
|
func (h *Handler) RegisterRoutes(
|
|
r chi.Router,
|
|
authenticator func(http.Handler) http.Handler,
|
|
) {
|
|
r.Route("/me/alerts", func(r chi.Router) {
|
|
r.Use(authenticator)
|
|
|
|
r.Get("/", h.list)
|
|
r.Post("/", h.create)
|
|
r.Patch("/{id}", h.update)
|
|
r.Delete("/{id}", h.delete)
|
|
|
|
r.Get("/history", h.history)
|
|
})
|
|
}
|
|
|
|
type ruleDTO struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
Topic string `json:"topic"`
|
|
Predicate string `json:"predicate"`
|
|
CooldownSec int `json:"cooldown_sec"`
|
|
Enabled bool `json:"enabled"`
|
|
CreatedAt string `json:"created_at"`
|
|
UpdatedAt string `json:"updated_at"`
|
|
}
|
|
|
|
func toRuleDTO(r Rule) ruleDTO {
|
|
return ruleDTO{
|
|
ID: r.ID,
|
|
Name: r.Name,
|
|
Topic: r.Topic,
|
|
Predicate: r.Predicate,
|
|
CooldownSec: r.CooldownSec,
|
|
Enabled: r.Enabled,
|
|
CreatedAt: r.CreatedAt.UTC().Format("2006-01-02T15:04:05Z"),
|
|
UpdatedAt: r.UpdatedAt.UTC().Format("2006-01-02T15:04:05Z"),
|
|
}
|
|
}
|
|
|
|
func (h *Handler) list(w http.ResponseWriter, r *http.Request) {
|
|
userID := middleware.GetUserID(r.Context())
|
|
rules, err := h.repo.ListByUser(r.Context(), userID)
|
|
if err != nil {
|
|
core.InternalServerError(w, err)
|
|
return
|
|
}
|
|
out := make([]ruleDTO, 0, len(rules))
|
|
for _, ru := range rules {
|
|
out = append(out, toRuleDTO(ru))
|
|
}
|
|
core.OK(w, out)
|
|
}
|
|
|
|
type createRuleRequest struct {
|
|
Name string `json:"name" validate:"required,min=1,max=200"`
|
|
Topic string `json:"topic" validate:"required,min=1,max=64"`
|
|
Predicate string `json:"predicate" validate:"omitempty,max=2048"`
|
|
CooldownSec int `json:"cooldown_sec" validate:"gte=0,lte=86400"`
|
|
Enabled bool `json:"enabled"`
|
|
}
|
|
|
|
func (h *Handler) create(w http.ResponseWriter, r *http.Request) {
|
|
userID := middleware.GetUserID(r.Context())
|
|
var req createRuleRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
core.BadRequest(w, "invalid request body")
|
|
return
|
|
}
|
|
if err := h.validator.Struct(req); err != nil {
|
|
core.BadRequest(w, core.FormatValidationError(err))
|
|
return
|
|
}
|
|
rule := &Rule{
|
|
UserID: userID,
|
|
Name: req.Name,
|
|
Topic: req.Topic,
|
|
Predicate: req.Predicate,
|
|
CooldownSec: req.CooldownSec,
|
|
Enabled: req.Enabled,
|
|
}
|
|
if err := h.repo.Create(r.Context(), rule); err != nil {
|
|
core.InternalServerError(w, err)
|
|
return
|
|
}
|
|
core.Created(w, toRuleDTO(*rule))
|
|
}
|
|
|
|
type updateRuleRequest struct {
|
|
Name *string `json:"name,omitempty" validate:"omitempty,min=1,max=200"`
|
|
Predicate *string `json:"predicate,omitempty" validate:"omitempty,max=2048"`
|
|
CooldownSec *int `json:"cooldown_sec,omitempty" validate:"omitempty,gte=0,lte=86400"`
|
|
Enabled *bool `json:"enabled,omitempty"`
|
|
}
|
|
|
|
func (h *Handler) update(w http.ResponseWriter, r *http.Request) {
|
|
userID := middleware.GetUserID(r.Context())
|
|
id := chi.URLParam(r, "id")
|
|
|
|
var req updateRuleRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
core.BadRequest(w, "invalid request body")
|
|
return
|
|
}
|
|
if err := h.validator.Struct(req); err != nil {
|
|
core.BadRequest(w, core.FormatValidationError(err))
|
|
return
|
|
}
|
|
|
|
rule, err := h.repo.Get(r.Context(), id, userID)
|
|
if err != nil {
|
|
if errors.Is(err, core.ErrNotFound) {
|
|
core.NotFound(w, "rule")
|
|
return
|
|
}
|
|
core.InternalServerError(w, err)
|
|
return
|
|
}
|
|
if req.Name != nil {
|
|
rule.Name = *req.Name
|
|
}
|
|
if req.Predicate != nil {
|
|
rule.Predicate = *req.Predicate
|
|
}
|
|
if req.CooldownSec != nil {
|
|
rule.CooldownSec = *req.CooldownSec
|
|
}
|
|
if req.Enabled != nil {
|
|
rule.Enabled = *req.Enabled
|
|
}
|
|
if err := h.repo.Update(r.Context(), rule); err != nil {
|
|
core.InternalServerError(w, err)
|
|
return
|
|
}
|
|
core.OK(w, toRuleDTO(*rule))
|
|
}
|
|
|
|
func (h *Handler) delete(w http.ResponseWriter, r *http.Request) {
|
|
userID := middleware.GetUserID(r.Context())
|
|
id := chi.URLParam(r, "id")
|
|
if err := h.repo.Delete(r.Context(), id, userID); err != nil {
|
|
if errors.Is(err, core.ErrNotFound) {
|
|
core.NotFound(w, "rule")
|
|
return
|
|
}
|
|
core.InternalServerError(w, err)
|
|
return
|
|
}
|
|
core.NoContent(w)
|
|
}
|
|
|
|
func (h *Handler) history(w http.ResponseWriter, r *http.Request) {
|
|
userID := middleware.GetUserID(r.Context())
|
|
limit := 50
|
|
if v := r.URL.Query().Get("limit"); v != "" {
|
|
if n, err := strconv.Atoi(v); err == nil && n > 0 && n <= 500 {
|
|
limit = n
|
|
}
|
|
}
|
|
rows, err := h.repo.RecentHistory(r.Context(), userID, limit)
|
|
if err != nil {
|
|
core.InternalServerError(w, err)
|
|
return
|
|
}
|
|
core.OK(w, rows)
|
|
}
|