package api import ( "net/http" "strconv" "archivdms/internal/audit" ) func (s *Server) handleAuditLog(w http.ResponseWriter, r *http.Request) { sess := sessionFromCtx(r.Context()) entries, total, err := s.audlog.Query(audit.QueryFilter{ TenantID: sess.TenantID, }) if err != nil { writeError(w, http.StatusInternalServerError, "audit query failed") return } writeJSON(w, http.StatusOK, map[string]any{"entries": entries, "total": total}) } // handleDocumentAuditLog returns the audit trail scoped to a single document // (GET /api/documents/{id}/audit). Unlike handleAuditLog (domain_admin+, full // tenant log) this is available to every authenticated user, but only after an // ownership/ACL check: GetDocument filters WHERE tenant_id (and the document // ACL), so a caller who may not see the document gets a 404 and never its // history. The document_id filter uses the exact same string format // (strconv.FormatInt(id, 10)) that document_handlers.go writes into the audit // entries, otherwise the filter would match nothing. func (s *Server) handleDocumentAuditLog(w http.ResponseWriter, r *http.Request) { sess := sessionFromCtx(r.Context()) id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) if err != nil || sess.TenantID == nil { writeError(w, http.StatusBadRequest, "invalid document id") return } // ACL/tenant check: only callers who may see the document may see its history. if _, err := s.store.GetDocument(r.Context(), id, *sess.TenantID); err != nil { writeError(w, http.StatusNotFound, "document not found") return } entries, total, err := s.audlog.Query(audit.QueryFilter{ TenantID: sess.TenantID, DocumentID: strconv.FormatInt(id, 10), }) if err != nil { writeError(w, http.StatusInternalServerError, "audit query failed") return } writeJSON(w, http.StatusOK, map[string]any{"entries": entries, "total": total}) }