92 lines
2.2 KiB
Go
92 lines
2.2 KiB
Go
package handlers
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"log"
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"chat-switchboard/models"
|
|
"chat-switchboard/store"
|
|
)
|
|
|
|
// AuditLog writes an audit entry via the store interface.
|
|
// Non-blocking: errors are logged but don't break the caller.
|
|
func AuditLog(auditStore store.AuditStore, c *gin.Context, action, resourceType, resourceID string, metadata map[string]interface{}) {
|
|
if auditStore == nil {
|
|
return
|
|
}
|
|
|
|
actorID := getUserID(c)
|
|
if actorID == "" {
|
|
return
|
|
}
|
|
|
|
var meta models.JSONMap
|
|
if metadata != nil {
|
|
b, _ := json.Marshal(metadata)
|
|
json.Unmarshal(b, &meta)
|
|
}
|
|
|
|
entry := &models.AuditEntry{
|
|
ActorID: &actorID,
|
|
Action: action,
|
|
ResourceType: resourceType,
|
|
ResourceID: resourceID,
|
|
Metadata: meta,
|
|
IPAddress: c.ClientIP(),
|
|
UserAgent: c.GetHeader("User-Agent"),
|
|
}
|
|
|
|
if err := auditStore.Log(context.Background(), entry); err != nil {
|
|
log.Printf("audit log error: %v", err)
|
|
}
|
|
}
|
|
|
|
// ── Admin Audit Viewer ──────────────────────
|
|
|
|
// ListAuditLog returns paginated audit entries with optional filters.
|
|
// GET /api/v1/admin/audit?page=1&per_page=50&action=user.create&actor_id=...&resource_type=...
|
|
func (h *AdminHandler) ListAuditLog(c *gin.Context) {
|
|
page, perPage, offset := parsePagination(c)
|
|
|
|
opts := store.AuditListOptions{
|
|
ListOptions: store.ListOptions{
|
|
Limit: perPage,
|
|
Offset: offset,
|
|
},
|
|
Action: c.Query("action"),
|
|
ActorID: c.Query("actor_id"),
|
|
ResourceType: c.Query("resource_type"),
|
|
}
|
|
|
|
entries, total, err := h.stores.Audit.List(c.Request.Context(), opts)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "query failed"})
|
|
return
|
|
}
|
|
if entries == nil {
|
|
entries = []models.AuditEntry{}
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"data": entries,
|
|
"total": total,
|
|
"page": page,
|
|
"per_page": perPage,
|
|
})
|
|
}
|
|
|
|
// ListAuditActions returns distinct action names for filter dropdowns.
|
|
// GET /api/v1/admin/audit/actions
|
|
func (h *AdminHandler) ListAuditActions(c *gin.Context) {
|
|
actions, err := h.stores.Audit.ListActions(c.Request.Context())
|
|
if err != nil {
|
|
c.JSON(http.StatusOK, gin.H{"actions": []string{}})
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{"actions": actions})
|
|
}
|