Spaces:
Runtime error
Runtime error
File size: 1,804 Bytes
857a91b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | package api
import (
"log"
"net"
"net/http"
"strings"
"github.com/agent-matrix/matrix-runtime/internal/store"
)
// clientIP best-effort extracts the caller IP, honoring common proxy headers
// (the API typically runs behind Cloudflare / an ingress).
func clientIP(r *http.Request) string {
if v := r.Header.Get("CF-Connecting-IP"); v != "" {
return v
}
if v := r.Header.Get("X-Forwarded-For"); v != "" {
return strings.TrimSpace(strings.Split(v, ",")[0])
}
if host, _, err := net.SplitHostPort(r.RemoteAddr); err == nil {
return host
}
return r.RemoteAddr
}
// audit records a sensitive action. It is best-effort: a nil store or a write
// error never affects the request outcome (only logged).
func (s *Server) audit(r *http.Request, workspaceID, actor, action, target, status string, meta map[string]any) {
if s.store == nil {
return
}
if err := s.store.RecordAudit(store.AuditEvent{
WorkspaceID: workspaceID,
Actor: actor,
Action: action,
Target: target,
IP: clientIP(r),
Status: status,
Meta: meta,
}); err != nil {
log.Printf("audit: could not record %s: %v", action, err)
}
}
// truncate shortens s to at most n characters for audit targets.
func truncate(s string, n int) string {
if len(s) <= n {
return s
}
return s[:n] + "…"
}
// handleCloudAudit returns the workspace's recent audit events.
func (s *Server) handleCloudAudit(w http.ResponseWriter, r *http.Request) {
u, ok := s.currentUser(r)
if !ok {
writeError(w, http.StatusUnauthorized, "not authenticated")
return
}
events, err := s.store.ListAudit(u.WorkspaceID, 200)
if err != nil {
writeError(w, http.StatusInternalServerError, "could not load audit log")
return
}
writeJSON(w, http.StatusOK, map[string]any{"events": events})
}
|