Bind preference writes and session-scoped project reads to the authenticated principal, and require an owned session_id for recategorize so empty ids cannot touch all messages. Co-authored-by: Cursor <cursoragent@cursor.com>
58 lines
1.3 KiB
Go
58 lines
1.3 KiB
Go
package server
|
|
|
|
// chat_recategorize_api.go — 管理端点 POST /api/v1/chat/recategorize (Spec §3.4 A4)
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
type recategorizeReq struct {
|
|
SessionID string `json:"session_id"`
|
|
}
|
|
|
|
func (s *Server) handleChatRecategorize(w http.ResponseWriter, r *http.Request) {
|
|
user, ok := s.requireAuthenticatedUser(w, r)
|
|
if !ok {
|
|
return
|
|
}
|
|
if s.ctxMgr == nil {
|
|
writeSafeError(w, r, http.StatusServiceUnavailable, "context manager unavailable")
|
|
return
|
|
}
|
|
|
|
var req recategorizeReq
|
|
if r.Body != nil {
|
|
_ = json.NewDecoder(r.Body).Decode(&req)
|
|
}
|
|
req.SessionID = strings.TrimSpace(req.SessionID)
|
|
if req.SessionID == "" {
|
|
writeJSONResponse(w, http.StatusBadRequest, map[string]any{
|
|
"success": false,
|
|
"error": "session_id is required",
|
|
})
|
|
return
|
|
}
|
|
if !s.isSystemAdminUser(r.Context(), user) {
|
|
if !s.assertSessionOwnedByUser(w, r, req.SessionID, user.ID) {
|
|
return
|
|
}
|
|
}
|
|
|
|
res, err := s.ctxMgr.RecategorizeMessages(r.Context(), req.SessionID)
|
|
if err != nil {
|
|
writeJSONResponse(w, http.StatusInternalServerError, map[string]any{
|
|
"success": false,
|
|
"error": err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
writeJSONResponse(w, http.StatusOK, map[string]any{
|
|
"success": true,
|
|
"total_processed": res.TotalProcessed,
|
|
"total_updated": res.TotalUpdated,
|
|
})
|
|
}
|