mirror of
https://github.com/RuoJi6/ARTEX.git
synced 2026-11-04 19:43:50 +08:00
83 lines
3.2 KiB
Go
83 lines
3.2 KiB
Go
package server
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/Autumn-27/artex/db"
|
|
)
|
|
|
|
// taskAssetMapping starts a visible asset-mapper conversation for the task and
|
|
// then lets the graph refresh independently. The graph itself remains sourced
|
|
// from the database; the conversation is the audit trail for the explanation.
|
|
func (s *Server) taskAssetMapping(w http.ResponseWriter, r *http.Request) {
|
|
t, ok := s.m.Task(r.PathValue("id"))
|
|
if !ok {
|
|
writeErr(w, http.StatusNotFound, "task not found")
|
|
return
|
|
}
|
|
if s.m.pg == nil || s.chatAgentRef() == nil {
|
|
writeErr(w, http.StatusServiceUnavailable, s.chatUnavailableReason())
|
|
return
|
|
}
|
|
if agentInfo, err := s.m.pg.GetAgentByKey("asset_mapper"); err != nil || agentInfo == nil || !agentInfo.Enabled {
|
|
writeErr(w, http.StatusServiceUnavailable, "资产梳理 Agent 未启用")
|
|
return
|
|
}
|
|
taskID, _ := strconv.ParseInt(t.ID, 10, 64)
|
|
message := fmt.Sprintf("请重新梳理任务 #%d 的资产关系。只使用当前任务及其直接关联任务的真实资产数据,按互联网入口、边界服务、内网业务三层说明关系;不要探测、不要修改资产库,也不要把没有证据的连接描述成已验证通路。", taskID)
|
|
if as := s.m.Assets(); as != nil {
|
|
if graph, err := as.BuildCoverageGraph(taskID, t.ExpID); err == nil {
|
|
type nodeSample struct {
|
|
Key, Kind, Label, Domain, IP string
|
|
Port int
|
|
Tested, InScope bool
|
|
}
|
|
counts := map[string]int{}
|
|
sample := make([]nodeSample, 0, 60)
|
|
for _, node := range graph.Nodes {
|
|
counts[node.Kind]++
|
|
if len(sample) < 60 {
|
|
sample = append(sample, nodeSample{
|
|
Key: node.Key, Kind: node.Kind, Label: node.Label, Domain: node.Domain,
|
|
IP: node.IP, Port: node.Port, Tested: node.Tested, InScope: node.InScope,
|
|
})
|
|
}
|
|
}
|
|
edges := graph.Edges
|
|
truncatedEdges := len(edges) > 80
|
|
if truncatedEdges {
|
|
edges = edges[:80]
|
|
}
|
|
snapshot, _ := json.Marshal(map[string]any{
|
|
"nodes_total": len(graph.Nodes), "edges_total": len(graph.Edges),
|
|
"counts_by_kind": counts, "node_sample": sample, "edge_sample": edges,
|
|
"truncated": len(graph.Nodes) > len(sample) || truncatedEdges,
|
|
})
|
|
message += "\n\n页面刚读取的任务资产图真实摘要如下;只依据摘要和任务范围解释,避免把全局资产误当成本任务资产。未列出的节点不要逐一补写:\n" + string(snapshot)
|
|
}
|
|
}
|
|
title := fmt.Sprintf("资产梳理 · 任务 #%d", taskID)
|
|
conversation, err := s.m.pg.CreateConversation("asset_mapper", title, nil)
|
|
if err != nil || conversation == nil {
|
|
writeErr(w, http.StatusInternalServerError, "创建资产梳理会话失败")
|
|
return
|
|
}
|
|
if _, err := s.m.pg.AppendConvActivity(conversation.ID, db.Activity{
|
|
Worker: "asset_mapper", Kind: "user", Summary: title, Detail: message,
|
|
}); err != nil {
|
|
writeErr(w, http.StatusInternalServerError, "写入资产梳理会话失败")
|
|
return
|
|
}
|
|
busyKey := s.convBusyKey(conversation.ID)
|
|
s.chatMu.Lock()
|
|
s.chatBusy[busyKey] = true
|
|
s.chatMu.Unlock()
|
|
s.runConversation(conversation, message, busyKey, message)
|
|
writeJSON(w, http.StatusAccepted, map[string]any{
|
|
"status": "accepted", "agent_key": "asset_mapper", "conversation_id": conversation.ID,
|
|
})
|
|
}
|