This repository has been archived on 2026-04-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
core/server/handlers/audit.go
Jeffrey Smith 680ec3b897
All checks were successful
CI/CD / detect-changes (push) Successful in 3s
CI/CD / test-frontend (push) Successful in 5s
CI/CD / test-go-pg (push) Successful in 2m34s
CI/CD / test-sqlite (push) Successful in 2m46s
CI/CD / build-and-deploy (push) Successful in 1m55s
Feat rebrand armature (#43)
Co-authored-by: Jeffrey Smith <jasafpro@gmail.com>
Co-committed-by: Jeffrey Smith <jasafpro@gmail.com>
2026-03-31 23:25:37 +00:00

92 lines
2.2 KiB
Go

package handlers
import (
"context"
"encoding/json"
"log"
"net/http"
"github.com/gin-gonic/gin"
"armature/models"
"armature/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})
}