Ignore request user_id as identity, authorize from the session Principal, and hydrate restore/memory from the same visible set so HTTP, SSE, and prompt assembly cannot amplify hidden messages. Co-authored-by: Cursor <cursoragent@cursor.com>
74 lines
1.9 KiB
Go
74 lines
1.9 KiB
Go
package server
|
|
|
|
// chat_export_api.go — P3 聊天记录导出查询 API
|
|
//
|
|
// GET /api/v1/chat/export/messages?session_id=...&date_from=...&date_to=...&category=...
|
|
//
|
|
// 返回按条件过滤后的消息列表,支持客户端导出为 docx/xlsx 等格式。
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
|
|
"chat-agent-system/internal/app/chatflow"
|
|
"chat-agent-system/internal/ctxmgr"
|
|
"chat-agent-system/internal/replyexport"
|
|
)
|
|
|
|
func (s *Server) handleChatExportMessages(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
|
|
user, ok := s.requireAuthenticatedUser(w, r)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
sessionID := strings.TrimSpace(r.URL.Query().Get("session_id"))
|
|
if sessionID == "" {
|
|
http.Error(w, "missing required query param 'session_id'", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if s.chatLog == nil {
|
|
http.Error(w, "context manager not initialized", http.StatusServiceUnavailable)
|
|
return
|
|
}
|
|
|
|
msgs, err := s.chatLog.Export(r.Context(), s.chatPrincipal(user, chatflow.ActionExport), chatflow.ExportQuery{
|
|
SessionID: sessionID,
|
|
DateFrom: strings.TrimSpace(r.URL.Query().Get("date_from")),
|
|
DateTo: strings.TrimSpace(r.URL.Query().Get("date_to")),
|
|
Category: strings.TrimSpace(r.URL.Query().Get("category")),
|
|
})
|
|
if err != nil {
|
|
s.writeChatLogError(w, err)
|
|
return
|
|
}
|
|
|
|
filtered := make([]ctxmgr.ChatMessageRecord, 0, len(msgs))
|
|
for _, m := range msgs {
|
|
hasCard := false
|
|
if m.Metadata != nil {
|
|
if _, exists := m.Metadata["interactive_card"]; exists {
|
|
hasCard = true
|
|
}
|
|
}
|
|
_, cap := replyexport.EvaluatePolicy(replyexport.PolicyOptions{
|
|
Role: m.Role,
|
|
Status: "completed",
|
|
Content: m.Content,
|
|
HasInteractiveCard: hasCard,
|
|
Revision: m.ContentRevision,
|
|
})
|
|
if cap.Policy == replyexport.PolicyNone {
|
|
continue
|
|
}
|
|
filtered = append(filtered, m)
|
|
}
|
|
|
|
writeJSONResponse(w, http.StatusOK, filtered)
|
|
}
|