mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-05-21 23:26:50 +02:00
147 lines
3.8 KiB
Go
147 lines
3.8 KiB
Go
package handler
|
|
|
|
import (
|
|
"net/http"
|
|
"time"
|
|
|
|
"cyberstrike-ai/internal/audit"
|
|
"cyberstrike-ai/internal/database"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// AuditHandler serves platform audit log APIs.
|
|
type AuditHandler struct {
|
|
db *database.DB
|
|
audit *audit.Service
|
|
logger *zap.Logger
|
|
}
|
|
|
|
// NewAuditHandler creates an audit log handler.
|
|
func NewAuditHandler(db *database.DB, auditSvc *audit.Service, logger *zap.Logger) *AuditHandler {
|
|
return &AuditHandler{db: db, audit: auditSvc, logger: logger}
|
|
}
|
|
|
|
// Meta GET /api/audit/meta
|
|
func (h *AuditHandler) Meta(c *gin.Context) {
|
|
enabled := false
|
|
retentionDays := 0
|
|
if h.audit != nil {
|
|
enabled = h.audit.Enabled()
|
|
retentionDays = h.audit.RetentionDays()
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"enabled": enabled,
|
|
"retention_days": retentionDays,
|
|
"default_page_size": 20,
|
|
"max_page_size": 100,
|
|
"max_export": 5000,
|
|
})
|
|
}
|
|
|
|
// Summary GET /api/audit/summary
|
|
func (h *AuditHandler) Summary(c *gin.Context) {
|
|
if h.db == nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "database unavailable"})
|
|
return
|
|
}
|
|
base := auditFilterFromQuery(c)
|
|
total, err := h.db.CountAuditLogs(base)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
failFilter := base
|
|
failFilter.Result = "failure"
|
|
failures, err := h.db.CountAuditLogs(failFilter)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
since := time.Now().AddDate(0, 0, -7)
|
|
recentFilter := base
|
|
recentFilter.Since = &since
|
|
recent7d, err := h.db.CountAuditLogs(recentFilter)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"total": total,
|
|
"failures": failures,
|
|
"recent_7d": recent7d,
|
|
"has_filters": c.Query("category") != "" || c.Query("action") != "" || c.Query("result") != "" ||
|
|
c.Query("q") != "" || c.Query("since") != "" || c.Query("until") != "",
|
|
})
|
|
}
|
|
|
|
// ListLogs GET /api/audit/logs
|
|
func (h *AuditHandler) ListLogs(c *gin.Context) {
|
|
if h.db == nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "database unavailable"})
|
|
return
|
|
}
|
|
filter := auditFilterFromQuery(c)
|
|
page, pageSize := auditPaginationFromQuery(c)
|
|
filter.Limit = pageSize
|
|
filter.Offset = (page - 1) * pageSize
|
|
|
|
logs, err := h.db.ListAuditLogs(filter)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
total, err := h.db.CountAuditLogs(filter)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"logs": logs,
|
|
"total": total,
|
|
"page": page,
|
|
"page_size": pageSize,
|
|
})
|
|
}
|
|
|
|
// GetLog GET /api/audit/logs/:id
|
|
func (h *AuditHandler) GetLog(c *gin.Context) {
|
|
if h.db == nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "database unavailable"})
|
|
return
|
|
}
|
|
row, err := h.db.GetAuditLogByID(c.Param("id"))
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "审计记录不存在"})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"log": row})
|
|
}
|
|
|
|
// ExportLogs GET /api/audit/logs/export — JSON or CSV (?format=csv), max 5000 rows.
|
|
func (h *AuditHandler) ExportLogs(c *gin.Context) {
|
|
if h.db == nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "database unavailable"})
|
|
return
|
|
}
|
|
filter := auditFilterFromQuery(c)
|
|
filter.Limit = 5000
|
|
filter.Offset = 0
|
|
|
|
logs, err := h.db.ListAuditLogs(filter)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
if c.Query("format") == "csv" {
|
|
writeAuditLogsCSV(c, logs)
|
|
return
|
|
}
|
|
c.Header("Content-Disposition", `attachment; filename="audit-logs.json"`)
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"exported_at": time.Now().UTC().Format(time.RFC3339),
|
|
"logs": logs,
|
|
})
|
|
}
|