feat(findings): 漏洞删除功能(二次确认)

- db: DeleteFinding(id) 删 findings 行 + 来源探索节点(kind='finding'),
  级联清理 edges/node_assets、置空 activity 引用;返回受影响行数。
- server: DELETE /api/exploration/findings/{id}。
- web: api.deleteFinding;三处均可删且带 AlertDialog 二次确认——
  发现列表页独立「操作」列(每行垃圾桶按钮)、漏洞详情页头部删除按钮、
  任务详情·发现 Tab 展开区删除按钮。两处列表同源,删一次全部消失。
- 测试 TestDeleteFinding:findings 行 + 来源节点均被删,重复删返回(0,nil)。

go build/vet、tsc 通过。
This commit is contained in:
Autumn-27 committed 2026-08-19 10:28:06 -04:00
1 parent 8047bb7aca
commit 90030051ce
7 files changed
+250 -8

No files matched your search

+21
View File
@@ -616,6 +616,7 @@ func (s *Server) Handler() http.Handler {
mux.HandleFunc("GET /api/exploration/findings/{id}", s.getFinding)
mux.HandleFunc("GET /api/exploration/findings/{id}/lineage", s.findingLineage)
mux.HandleFunc("PATCH /api/exploration/findings/{id}", s.patchFinding)
mux.HandleFunc("DELETE /api/exploration/findings/{id}", s.deleteFinding)
mux.HandleFunc("GET /api/exploration/intents", s.intents)
mux.HandleFunc("GET /api/exploration/graph", s.explorationGraph)
mux.HandleFunc("GET /api/exploration/activity", s.activity)
@@ -1623,6 +1624,26 @@ func (s *Server) patchFinding(w http.ResponseWriter, r *http.Request) {
writeJSON(w, 200, findingFromDB(f, s.resolveAssetIDs(f.AssetIDs)))
}
// deleteFinding removes a finding (findings row + originating exploration node).
// id is the standalone findings-table id (DTO finding_id).
func (s *Server) deleteFinding(w http.ResponseWriter, r *http.Request) {
id := int64(atoiDefault(r.PathValue("id"), 0))
if id <= 0 {
writeErr(w, 400, "bad finding id")
return
}
n, err := s.m.pg.DeleteFinding(id)
if err != nil {
writeErr(w, 500, err.Error())
return
}
if n == 0 {
writeErr(w, 404, "finding not found")
return
}
writeJSON(w, 200, map[string]any{"deleted": true, "id": id})
}
func (s *Server) intents(w http.ResponseWriter, r *http.Request) {
t := s.m.ResolveTask(r.URL.Query().Get("task"))
if t == nil {