readysite / website / internal / helpers / audit.go
2.5 KB
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)
}
← Back