mirror of
https://github.com/syntrex-lab/gomcp.git
synced 2026-04-24 20:06:21 +02:00
377 lines
9.2 KiB
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),
|
|
})
|
|
}
|