diff --git a/server_handlers.go b/server_handlers.go index 2bd5eb2..0ae0b1c 100644 --- a/server_handlers.go +++ b/server_handlers.go @@ -38,22 +38,31 @@ func (s *Server) handleGetAuditLog(msg map[string]interface{}) (map[string]inter filterNetID := jsonUint16(msg, "network_id") limit := 100 - if l, ok := msg["limit"].(float64); ok && l > 0 && l <= 1000 { + if l, ok := msg["limit"].(float64); ok && l > 0 && l <= 100 { limit = int(l) } + offset := 0 + if o, ok := msg["offset"].(float64); ok && o >= 0 { + offset = int(o) + } s.auditMu.Lock() all := make([]AuditEntry, len(s.auditLog)) copy(all, s.auditLog) s.auditMu.Unlock() - // Filter and reverse (newest first) + // Filter and reverse (newest first). Skip offset-matched entries before collecting. var entries []map[string]interface{} + skipped := 0 for i := len(all) - 1; i >= 0 && len(entries) < limit; i-- { e := all[i] if filterNetID != 0 && e.NetworkID != filterNetID { continue } + if skipped < offset { + skipped++ + continue + } m := map[string]interface{}{ "timestamp": e.Timestamp, "action": e.Action, diff --git a/zz_handlers_more_test.go b/zz_handlers_more_test.go index f50f2d2..5520e25 100644 --- a/zz_handlers_more_test.go +++ b/zz_handlers_more_test.go @@ -336,6 +336,52 @@ func TestServer_HandleGetAuditLog_FiltersByNetwork(t *testing.T) { } } +func TestServer_HandleGetAuditLog_LimitCappedAt100(t *testing.T) { + t.Parallel() + s := newTestServer(t, "admin") + for i := 0; i < 200; i++ { + s.appendAudit("evt", 0, 0) + } + resp, err := s.handleGetAuditLog(map[string]interface{}{ + "admin_token": "admin", + "limit": float64(999), + }) + if err != nil { + t.Fatal(err) + } + entries := resp["entries"].([]map[string]interface{}) + if len(entries) > 100 { + t.Fatalf("limit capped: got %d entries, want <= 100", len(entries)) + } +} + +func TestServer_HandleGetAuditLog_OffsetsPastFirstPage(t *testing.T) { + t.Parallel() + s := newTestServer(t, "admin") + s.appendAudit("oldest", 0, 0) + s.appendAudit("middle", 0, 0) + s.appendAudit("newest", 0, 0) + resp, err := s.handleGetAuditLog(map[string]interface{}{ + "admin_token": "admin", + "limit": float64(2), + "offset": float64(1), + }) + if err != nil { + t.Fatal(err) + } + entries := resp["entries"].([]map[string]interface{}) + if len(entries) != 2 { + t.Fatalf("offset+limit: got %d entries, want 2", len(entries)) + } + // With offset=1, newest is skipped; should get middle, then oldest + if entries[0]["action"] != "middle" { + t.Fatalf("first after offset: %v, want middle", entries[0]["action"]) + } + if entries[1]["action"] != "oldest" { + t.Fatalf("second after offset: %v, want oldest", entries[1]["action"]) + } +} + // --- handleBeaconRegister ----------------------------------------------- func TestServer_HandleBeaconRegister_RequiresAdmin(t *testing.T) {