package handlers import ( "net/http" "strconv" "time" "xorm.io/xorm" "github.com/forgeo/forgebucket/internal/api/middleware" "github.com/forgeo/forgebucket/internal/models" ) type AuditHandler struct{ db *xorm.Engine } func NewAuditHandler(db *xorm.Engine) *AuditHandler { return &AuditHandler{db: db} } // List returns recent audit log entries. Admin-only. // Query params: limit (default 50, max 200), before (ID cursor for pagination), // actor_id, method, since (RFC3339). func (h *AuditHandler) List(w http.ResponseWriter, r *http.Request) { isAdmin, _ := r.Context().Value(middleware.ContextKeyIsAdmin).(bool) if !isAdmin { jsonError(w, "admin access required", http.StatusForbidden) return } q := r.URL.Query() limit := 50 if l, err := strconv.Atoi(q.Get("limit")); err == nil && l > 0 { if l > 200 { l = 200 } limit = l } sess := h.db.Desc("id").Limit(limit) if before, err := strconv.ParseInt(q.Get("before"), 10, 64); err == nil && before > 0 { sess = sess.And("id < ?", before) } if actorID, err := strconv.ParseInt(q.Get("actor_id"), 10, 64); err == nil && actorID > 0 { sess = sess.And("actor_id = ?", actorID) } if method := q.Get("method"); method != "" { sess = sess.And("method = ?", method) } if since := q.Get("since"); since != "" { if t, err := time.Parse(time.RFC3339, since); err == nil { sess = sess.And("occurred_at >= ?", t.UTC()) } } var entries []models.AuditLog if err := sess.Find(&entries); err != nil { jsonError(w, "could not fetch audit log", http.StatusInternalServerError) return } if entries == nil { entries = []models.AuditLog{} } jsonOK(w, entries) }