Skip to content

Commit

Permalink
webapp/events: add filter strip and filter buttons
Browse files Browse the repository at this point in the history
for:
- src and est ip
- event type
- @from and @to timestamp
  • Loading branch information
jasonish committed Dec 30, 2024
1 parent 222572d commit a333c2a
Show file tree
Hide file tree
Showing 5 changed files with 253 additions and 135 deletions.
47 changes: 11 additions & 36 deletions webapp/src/Alerts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,12 @@ import {
eventSetEscalated,
Tag,
} from "./event";
import { AddressCell, TimestampCell } from "./TimestampCell";
import { IdleTimer } from "./idletimer";
import { eventStore } from "./eventstore";
import { Logger } from "./util";
import { SensorSelect } from "./common/SensorSelect";
import * as bootstrap from "bootstrap";
import { FilterStrip } from "./components";
import { AddressCell, FilterStrip, TimestampCell } from "./components";

const DEFAULT_SORTBY = "timestamp";
const DEFAULT_SORTORDER = "desc";
Expand Down Expand Up @@ -374,7 +373,6 @@ export function Alerts() {
untrack(() => {
const logger = new Logger("Alerts.refreshEvents", true);
let qFilters = getFilters();
console.log(qFilters);
let q: undefined | string = qFilters.join(" ");

if (searchParams.q) {
Expand Down Expand Up @@ -670,34 +668,8 @@ export function Alerts() {
});
}

function removeFilter(filter: string) {
// TODO: There is a cleaner way with closure to do this.
let newFilters = filters();
newFilters = newFilters.filter((f: any) => f !== filter);

// But we have an effect to do this.. But it might not happen
// until after the refresh. Need to revisit effect/reactive
// dependency chains here.
setFilters(newFilters);

if (newFilters.length == 0) {
console.log("Setting filters to null");
setSearchParams({
filters: undefined,
});
} else {
setSearchParams({
filters: newFilters,
});
}
}

const clearFilters = () => {
setFilters([]);
setSearchParams({ filters: null });
};

// Getter for searchParams.filters to convert to an array if there is only one "filters" parameter.
// Getter for searchParams.filters to convert to an array if there
// is only one "filters" parameter.
const getFilters = createMemo(() => {
let filters = searchParams.filters || [];

Expand All @@ -713,6 +685,13 @@ export function Alerts() {
setFilters(getFilters());
});

// Effect to update the query parameters when the filters signal updates.
createEffect(() => {
setSearchParams({
filters: filters().length == 0 ? undefined : filters(),
});
});

function updateSort(key: string) {
console.log("Sorting by " + key);
let order = getSortOrder();
Expand Down Expand Up @@ -872,11 +851,7 @@ export function Alerts() {

{/* Filter strip. */}
<Show when={filters().length > 0}>
<FilterStrip
filters={filters}
clear={clearFilters}
remove={removeFilter}
/>
<FilterStrip filters={filters} setFilters={setFilters} />
</Show>

<div
Expand Down
111 changes: 104 additions & 7 deletions webapp/src/Events.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
// SPDX-FileCopyrightText: (C) 2023 Jason Ish <[email protected]>
// SPDX-License-Identifier: MIT

import { TIME_RANGE, Top } from "./Top";
import { Top } from "./Top";
import * as API from "./api";
import {
createEffect,
createMemo,
createSignal,
For,
Match,
Expand All @@ -15,16 +16,16 @@ import {
} from "solid-js";
import { EventWrapper } from "./types";
import { Button, Col, Container, Form, Row } from "solid-bootstrap";
import { AddressCell, TimestampCell } from "./TimestampCell";
import { useNavigate, useSearchParams } from "@solidjs/router";
import { formatEventDescription } from "./formatters";
import { BiCaretRightFill } from "./icons";
import { BiCaretRightFill, BiDashCircle, BiPlusCircle } from "./icons";
import tinykeys from "tinykeys";
import { scrollToClass } from "./scroll";
import { Transition } from "solid-transition-group";
import { eventIsArchived, eventSetArchived } from "./event";
import { AlertDescription } from "./Alerts";
import { EventsQueryParams } from "./api";
import { AddressCell, FilterStrip, TimestampCell } from "./components";

// The list of event types that will be shown in dropdowns.
export const EVENT_TYPES: { name: string; eventType: string }[] = [
Expand Down Expand Up @@ -87,8 +88,10 @@ export function Events() {
event_type?: string;
from?: string;
to?: string;
filters?: string[];
}>();
const [cursor, setCursor] = createSignal(0);
const [filters, setFilters] = createSignal<string[]>([]);
let keybindings: any = null;

onMount(() => {
Expand Down Expand Up @@ -131,6 +134,23 @@ export function Events() {
}
});

// Getter for searchParams.filters to convert to an array if there
// is only one "filters" parameter.
const getFilters = createMemo(() => {
let filters = searchParams.filters || [];

if (!Array.isArray(filters)) {
return [filters];
} else {
return filters;
}
});

// Effect to update the filter strip based on the filters in the query string.
createEffect(() => {
setFilters(getFilters());
});

createEffect(() => {
loadEvents();
});
Expand All @@ -144,15 +164,23 @@ export function Events() {
}

function loadEvents() {
let params: EventsQueryParams = {};
let params: EventsQueryParams = {
query_string: searchParams.q || "",
};

if (searchParams.event_type) {
params.event_type = searchParams.event_type;
setEventType(params.event_type);
}

if (searchParams.q) {
params.query_string = searchParams.q;
/* if (searchParams.q) {
* params.query_string = searchParams.q;
* } */

//const filterQuery: string = getFilters()?.join(" ");
const filterQuery: string = filters()?.join(" ");
if (filterQuery && filterQuery.length > 0) {
params.query_string += " " + filterQuery;
}

if (searchParams.order) {
Expand Down Expand Up @@ -252,6 +280,39 @@ export function Events() {
});
}

function addFilter(what: string, op: string, value: any) {
if (op == "+") {
op = "";
}
let entry: string = "";
if (typeof value === "number") {
entry = `${op}${what}:${value}`;
} else if (value.includes(" ")) {
entry = `${op}${what}:"${value}"`;
} else {
entry = `${op}${what}:${value}`;
}

let newFilters = filters();

// If if entry already exists.
if (newFilters.indexOf(entry) > -1) {
return;
}

newFilters.push(entry);
setSearchParams({
filters: newFilters,
});
}

// Effect to update the query parameters when the filters signal updates.
createEffect(() => {
setSearchParams({
filters: filters().length == 0 ? undefined : filters(),
});
});

return (
<>
<Top disableRange />
Expand Down Expand Up @@ -357,6 +418,10 @@ export function Events() {
</Col>
</Row>

<Show when={filters().length != 0}>
<FilterStrip filters={filters} setFilters={setFilters} />
</Show>

<Row>
<div class={"col-md-6 col-sm-12 mt-2 me-auto"}></div>
<div class={"col-md-6 col-sm-12 mt-2"}>
Expand Down Expand Up @@ -438,16 +503,48 @@ export function Events() {
<td class={"col-timestamp"}>
<TimestampCell
timestamp={event._source.timestamp}
addFilter={addFilter}
/>
</td>
<td class={"col-event-type"}>
{event._source.event_type?.toUpperCase() ||
"???"}
<span
class="show-on-hover ms-1"
onClick={(e) => {
e.stopPropagation();
addFilter(
"event_type",
"+",
event._source.event_type
);
}}
title={`Filter for event_type: ${event._source.event_type}`}
>
<BiPlusCircle />
</span>
<span
class="show-on-hover ms-1"
onClick={(e) => {
e.stopPropagation();
addFilter(
"event_type",
"-",
event._source.event_type
);
}}
title={`Filter out event_type: ${event._source.event_type}`}
>
<BiDashCircle />
</span>
</td>
<td class={"col-address"} style={"width: 0%;"}>
<Switch fallback={<>{event._source.host}</>}>
<Match when={event._source.src_ip}>
<AddressCell source={event._source} />
<AddressCell
source={event._source}
fn={addFilter}
/>
</Match>
<Match
when={
Expand Down
85 changes: 0 additions & 85 deletions webapp/src/TimestampCell.tsx

This file was deleted.

Loading

0 comments on commit a333c2a

Please sign in to comment.