gomcp/internal/transport/http/shadow_ai_handlers.go

377 lines
9.2 KiB
Go

package httpserver
import (
"encoding/json"
"io"
"net/http"
"strconv"
"time"
shadowai "github.com/syntrex-lab/gomcp/internal/application/shadow_ai"
)
// --- GET /api/v1/shadow-ai/stats ---
func (s *Server) handleShadowAIStats(w http.ResponseWriter, r *http.Request) {
if s.shadowAI == nil {
writeError(w, http.StatusServiceUnavailable, "shadow AI module not configured")
return
}
timeRange := r.URL.Query().Get("range")
if timeRange == "" {
timeRange = "24h"
}
stats := s.shadowAI.GetStats(timeRange)
writeJSON(w, http.StatusOK, stats)
}
// --- GET /api/v1/shadow-ai/events ---
func (s *Server) handleShadowAIEvents(w http.ResponseWriter, r *http.Request) {
if s.shadowAI == nil {
writeError(w, http.StatusServiceUnavailable, "shadow AI module not configured")
return
}
limit := 50
if v := r.URL.Query().Get("limit"); v != "" {
if parsed, err := strconv.Atoi(v); err == nil && parsed > 0 {
limit = parsed
}
}
if limit > 500 {
limit = 500
}
events := s.shadowAI.GetEvents(limit)
if events == nil {
events = []shadowai.ShadowAIEvent{}
}
writeJSON(w, http.StatusOK, map[string]any{
"events": events,
"count": len(events),
"limit": limit,
})
}
// --- GET /api/v1/shadow-ai/events/{id} ---
func (s *Server) handleShadowAIEventDetail(w http.ResponseWriter, r *http.Request) {
if s.shadowAI == nil {
writeError(w, http.StatusServiceUnavailable, "shadow AI module not configured")
return
}
id := r.PathValue("id")
if id == "" {
writeError(w, http.StatusBadRequest, "event id required")
return
}
event, ok := s.shadowAI.GetEvent(id)
if !ok {
writeError(w, http.StatusNotFound, "event not found")
return
}
writeJSON(w, http.StatusOK, event)
}
// --- POST /api/v1/shadow-ai/block ---
func (s *Server) handleShadowAIBlock(w http.ResponseWriter, r *http.Request) {
if s.shadowAI == nil {
writeError(w, http.StatusServiceUnavailable, "shadow AI module not configured")
return
}
var req struct {
TargetType string `json:"target_type"` // "domain", "ip", "host"
Target string `json:"target"`
Duration string `json:"duration"` // "24h", "48h", etc.
Reason string `json:"reason"`
}
limitBody(w, r)
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeError(w, http.StatusBadRequest, "invalid JSON: "+err.Error())
return
}
if req.TargetType == "" || req.Target == "" {
writeError(w, http.StatusBadRequest, "target_type and target are required")
return
}
duration := 24 * time.Hour
if req.Duration != "" {
if d, err := time.ParseDuration(req.Duration); err == nil {
duration = d
}
}
blockedBy := r.Header.Get("X-User-ID")
if blockedBy == "" {
blockedBy = "api"
}
err := s.shadowAI.ManualBlock(r.Context(), shadowai.BlockRequest{
TargetType: req.TargetType,
Target: req.Target,
Duration: duration,
Reason: req.Reason,
BlockedBy: blockedBy,
})
if err != nil {
writeError(w, http.StatusInternalServerError, "block failed: "+err.Error())
return
}
writeJSON(w, http.StatusOK, map[string]string{"status": "blocked", "target": req.Target})
}
// --- POST /api/v1/shadow-ai/unblock ---
func (s *Server) handleShadowAIUnblock(w http.ResponseWriter, r *http.Request) {
if s.shadowAI == nil {
writeError(w, http.StatusServiceUnavailable, "shadow AI module not configured")
return
}
var req struct {
TargetType string `json:"target_type"`
Target string `json:"target"`
}
limitBody(w, r)
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeError(w, http.StatusBadRequest, "invalid JSON: "+err.Error())
return
}
writeJSON(w, http.StatusOK, map[string]string{"status": "unblocked", "target": req.Target})
}
// --- POST /api/v1/shadow-ai/scan ---
func (s *Server) handleShadowAIScan(w http.ResponseWriter, r *http.Request) {
if s.shadowAI == nil {
writeError(w, http.StatusServiceUnavailable, "shadow AI module not configured")
return
}
body, err := io.ReadAll(io.LimitReader(r.Body, 1<<20))
if err != nil {
writeError(w, http.StatusBadRequest, "failed to read body")
return
}
var req struct {
Content string `json:"content"`
}
if err := json.Unmarshal(body, &req); err != nil {
writeError(w, http.StatusBadRequest, "invalid JSON: "+err.Error())
return
}
result := s.shadowAI.ScanContent(req.Content)
writeJSON(w, http.StatusOK, map[string]any{
"detected": result != "",
"key_type": result,
"timestamp": time.Now(),
})
}
// --- GET /api/v1/shadow-ai/integrations ---
func (s *Server) handleShadowAIIntegrations(w http.ResponseWriter, r *http.Request) {
if s.shadowAI == nil {
writeError(w, http.StatusServiceUnavailable, "shadow AI module not configured")
return
}
health := s.shadowAI.IntegrationHealth()
writeJSON(w, http.StatusOK, map[string]any{
"integrations": health,
"count": len(health),
})
}
// --- GET /api/v1/shadow-ai/integrations/{vendor}/health ---
func (s *Server) handleShadowAIVendorHealth(w http.ResponseWriter, r *http.Request) {
if s.shadowAI == nil {
writeError(w, http.StatusServiceUnavailable, "shadow AI module not configured")
return
}
vendor := r.PathValue("vendor")
if vendor == "" {
writeError(w, http.StatusBadRequest, "vendor required")
return
}
health, ok := s.shadowAI.VendorHealth(vendor)
if !ok {
writeError(w, http.StatusNotFound, "vendor not found")
return
}
writeJSON(w, http.StatusOK, health)
}
// --- GET /api/v1/shadow-ai/compliance ---
func (s *Server) handleShadowAICompliance(w http.ResponseWriter, r *http.Request) {
if s.shadowAI == nil {
writeError(w, http.StatusServiceUnavailable, "shadow AI module not configured")
return
}
period := r.URL.Query().Get("period")
if period == "" {
period = "30d"
}
report := s.shadowAI.GenerateComplianceReport(period)
writeJSON(w, http.StatusOK, report)
}
// --- POST /api/v1/shadow-ai/doc-review ---
func (s *Server) handleShadowAIDocReview(w http.ResponseWriter, r *http.Request) {
if s.shadowAI == nil {
writeError(w, http.StatusServiceUnavailable, "shadow AI module not configured")
return
}
var req struct {
DocID string `json:"doc_id"`
Content string `json:"content"`
UserID string `json:"user_id"`
}
limitBody(w, r)
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeError(w, http.StatusBadRequest, "invalid JSON: "+err.Error())
return
}
if req.Content == "" {
writeError(w, http.StatusBadRequest, "content is required")
return
}
if req.DocID == "" {
req.DocID = "doc-" + time.Now().Format("20060102-150405")
}
if req.UserID == "" {
req.UserID = r.Header.Get("X-User-ID")
}
result, approval := s.shadowAI.ReviewDocument(req.DocID, req.Content, req.UserID)
resp := map[string]any{
"scan_result": result,
}
if approval != nil {
resp["approval"] = approval
}
writeJSON(w, http.StatusOK, resp)
}
// --- GET /api/v1/shadow-ai/doc-review/{id} ---
func (s *Server) handleShadowAIDocReviewStatus(w http.ResponseWriter, r *http.Request) {
if s.shadowAI == nil {
writeError(w, http.StatusServiceUnavailable, "shadow AI module not configured")
return
}
id := r.PathValue("id")
if id == "" {
writeError(w, http.StatusBadRequest, "doc_id required")
return
}
result, ok := s.shadowAI.DocBridge().GetReview(id)
if !ok {
writeError(w, http.StatusNotFound, "review not found")
return
}
writeJSON(w, http.StatusOK, result)
}
// --- POST /api/v1/shadow-ai/approvals/{id}/verdict ---
func (s *Server) handleShadowAIApprovalVerdict(w http.ResponseWriter, r *http.Request) {
if s.shadowAI == nil {
writeError(w, http.StatusServiceUnavailable, "shadow AI module not configured")
return
}
id := r.PathValue("id")
if id == "" {
writeError(w, http.StatusBadRequest, "approval id required")
return
}
var req struct {
Verdict string `json:"verdict"` // "approve" or "deny"
Reason string `json:"reason"`
}
limitBody(w, r)
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeError(w, http.StatusBadRequest, "invalid JSON: "+err.Error())
return
}
analyst := r.Header.Get("X-User-ID")
if analyst == "" {
analyst = "api"
}
var err error
switch req.Verdict {
case "approve":
err = s.shadowAI.ApprovalEngine().Approve(id, analyst)
case "deny":
err = s.shadowAI.ApprovalEngine().Deny(id, analyst, req.Reason)
default:
writeError(w, http.StatusBadRequest, "verdict must be 'approve' or 'deny'")
return
}
if err != nil {
writeError(w, http.StatusNotFound, err.Error())
return
}
writeJSON(w, http.StatusOK, map[string]string{"status": req.Verdict + "d", "request_id": id})
}
// --- GET /api/v1/shadow-ai/approvals ---
func (s *Server) handleShadowAIPendingApprovals(w http.ResponseWriter, r *http.Request) {
if s.shadowAI == nil {
writeError(w, http.StatusServiceUnavailable, "shadow AI module not configured")
return
}
pending := s.shadowAI.ApprovalEngine().PendingRequests()
stats := s.shadowAI.ApprovalEngine().Stats()
writeJSON(w, http.StatusOK, map[string]any{
"pending": pending,
"stats": stats,
})
}
// --- GET /api/v1/shadow-ai/approvals/tiers ---
func (s *Server) handleShadowAIApprovalTiers(w http.ResponseWriter, r *http.Request) {
if s.shadowAI == nil {
writeError(w, http.StatusServiceUnavailable, "shadow AI module not configured")
return
}
tiers := s.shadowAI.ApprovalEngine().Tiers()
writeJSON(w, http.StatusOK, map[string]any{
"tiers": tiers,
"count": len(tiers),
})
}