audit.go
package helpers
import (
"encoding/json"
"log"
"net/http"
"github.com/readysite/readysite/website/models"
)
// Audit action constants.
const (
ActionCreate = "create"
ActionRead = "read"
ActionUpdate = "update"
ActionDelete = "delete"
)
// Sentinel user IDs for non-user actions.
const (
UserSystem = "system" // For system-initiated actions
UserAnonymous = "anonymous" // For anonymous user actions
)
// Audit resource type constants.
const (
ResourcePage = "page"
ResourceCollection = "collection"
ResourceDocument = "document"
ResourceFile = "file"
ResourceUser = "user"
)
// AuditLog records an action in the audit log.
func AuditLog(r *http.Request, userID, action, resourceType, resourceID, resourceName string, details any) {
// Use sentinel value for empty userID to make queries consistent
if userID == "" {
userID = UserAnonymous
}
// Get client IP
ip := r.RemoteAddr
if forwarded := r.Header.Get("X-Forwarded-For"); forwarded != "" {
ip = forwarded
}
// Convert details to JSON
var detailsJSON string
if details != nil {
data, err := json.Marshal(details)
if err != nil {
log.Printf("[audit] Failed to marshal details: %v", err)
detailsJSON = "{}"
} else {
detailsJSON = string(data)
}
}
entry := &models.AuditLog{
UserID: userID,
Action: action,
ResourceType: resourceType,
ResourceID: resourceID,
ResourceName: resourceName,
Details: detailsJSON,
IP: ip,
}
if _, err := models.AuditLogs.Insert(entry); err != nil {
log.Printf("[audit] Failed to save audit log: %v", err)
}
}
// AuditCreate is a convenience wrapper for create actions.
func AuditCreate(r *http.Request, userID, resourceType, resourceID, resourceName string) {
AuditLog(r, userID, ActionCreate, resourceType, resourceID, resourceName, nil)
}
// AuditRead is a convenience wrapper for read actions.
func AuditRead(r *http.Request, userID, resourceType, resourceID, resourceName string) {
AuditLog(r, userID, ActionRead, resourceType, resourceID, resourceName, nil)
}
// AuditUpdate is a convenience wrapper for update actions.
func AuditUpdate(r *http.Request, userID, resourceType, resourceID, resourceName string, changes any) {
AuditLog(r, userID, ActionUpdate, resourceType, resourceID, resourceName, changes)
}
// AuditDelete is a convenience wrapper for delete actions.
func AuditDelete(r *http.Request, userID, resourceType, resourceID, resourceName string) {
AuditLog(r, userID, ActionDelete, resourceType, resourceID, resourceName, nil)
}