package api import ( "encoding/json" "net/http" "strings" "git.pengzhan.dev/haopengzhan/rsspipe/internal/db" "git.pengzhan.dev/haopengzhan/rsspipe/internal/events" "git.pengzhan.dev/haopengzhan/rsspipe/internal/models" "git.pengzhan.dev/haopengzhan/rsspipe/internal/rules" "github.com/go-chi/chi/v5" ) // Rules API Handlers func (h *Handler) ListRules(w http.ResponseWriter, r *http.Request) { rulesList, err := h.db.ListRules() if err != nil { h.writeError(w, http.StatusInternalServerError, err.Error()) return } if rulesList == nil { rulesList = []models.Rule{} } h.writeJSON(w, http.StatusOK, rulesList) } func (h *Handler) CreateRule(w http.ResponseWriter, r *http.Request) { var body models.Rule if err := json.NewDecoder(r.Body).Decode(&body); err != nil { h.writeError(w, http.StatusBadRequest, "invalid rule payload") return } body.Name = strings.TrimSpace(body.Name) if body.Name == "" { h.writeError(w, http.StatusBadRequest, "rule name is required") return } if body.Trigger == "" { body.Trigger = "on_update" } if body.MatchMode == "" { body.MatchMode = "all" } if err := rules.ValidateRuleSafety(body); err != nil { h.writeError(w, http.StatusBadRequest, err.Error()) return } rule, err := h.db.CreateRule(body.Name, body.Trigger, body.MatchMode, body.Conditions, body.Actions) if err != nil { h.writeError(w, http.StatusInternalServerError, err.Error()) return } h.writeJSON(w, http.StatusCreated, rule) } func (h *Handler) UpdateRule(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") var body models.Rule if err := json.NewDecoder(r.Body).Decode(&body); err != nil { h.writeError(w, http.StatusBadRequest, "invalid rule payload") return } body.Name = strings.TrimSpace(body.Name) if body.Name == "" { h.writeError(w, http.StatusBadRequest, "rule name is required") return } if err := rules.ValidateRuleSafety(body); err != nil { h.writeError(w, http.StatusBadRequest, err.Error()) return } rule, err := h.db.UpdateRule(id, body.Name, body.Trigger, body.MatchMode, body.Conditions, body.Actions, body.IsActive) if err != nil { h.writeError(w, http.StatusInternalServerError, err.Error()) return } h.writeJSON(w, http.StatusOK, rule) } func (h *Handler) DeleteRule(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") if err := h.db.DeleteRule(id); err != nil { h.writeError(w, http.StatusInternalServerError, err.Error()) return } h.writeJSON(w, http.StatusOK, map[string]string{"message": "rule deleted"}) } func (h *Handler) ApplyRule(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") rule, err := h.db.GetRule(id) if err != nil { h.writeError(w, http.StatusNotFound, "rule not found") return } entries, _, err := h.db.ListEntries(db.EntryFilter{Limit: 200}) if err != nil { h.writeError(w, http.StatusInternalServerError, err.Error()) return } eval := rules.NewEvaluator() matchedCount := 0 for _, entry := range entries { if eval.Evaluate(*rule, entry) { matchedCount++ if h.bus != nil { h.bus.Publish(r.Context(), events.Event{ Type: events.EventManualTrigger, Entry: entry, Depth: 0, }) } } } h.writeJSON(w, http.StatusOK, map[string]interface{}{ "message": "rule applied to matching articles", "matched_count": matchedCount, "queued_jobs": matchedCount * len(rule.Actions), }) }