feat: implement rule application engine with full-text variant support, batch entry processing, and tag visualization in UI.

This commit is contained in:
2026-08-25 22:57:48 -07:00
parent 235966e933
commit 83c70cc3b9
10 changed files with 667 additions and 107 deletions
+49
View File
@@ -722,3 +722,52 @@ func (h *Handler) DeleteRule(w http.ResponseWriter, r *http.Request) {
}
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
}
evaluator := rules.NewEvaluator()
matchedCount := 0
queuedJobs := 0
// Paginate through ALL entries in batches of 500
batchSize := 500
offset := 0
for {
entries, total, err := h.db.ListEntries(db.EntryFilter{
View: "all",
Limit: batchSize,
Offset: offset,
})
if err != nil {
h.writeError(w, http.StatusInternalServerError, "failed to query entries: "+err.Error())
return
}
for _, entry := range entries {
if evaluator.Evaluate(*rule, entry) {
matchedCount++
for _, action := range rule.Actions {
h.pool.Dispatch(r.Context(), action, entry, 0)
queuedJobs++
}
}
}
offset += batchSize
if offset >= total || len(entries) == 0 {
break
}
}
h.writeJSON(w, http.StatusOK, map[string]interface{}{
"message": fmt.Sprintf("Rule applied to %d matching post(s). %d action(s) queued.", matchedCount, queuedJobs),
"matched_count": matchedCount,
"queued_jobs": queuedJobs,
})
}
+1
View File
@@ -75,6 +75,7 @@ func NewRouter(h *Handler, authService *auth.AuthService, staticDir string) http
r.Post("/rules", h.CreateRule)
r.Put("/rules/{id}", h.UpdateRule)
r.Delete("/rules/{id}", h.DeleteRule)
r.Post("/rules/{id}/apply", h.ApplyRule)
// OPML
r.Post("/opml/import", h.ImportOPML)