diff --git a/src/poller/poller.test.ts b/src/poller/poller.test.ts new file mode 100644 index 0000000..81cfc43 --- /dev/null +++ b/src/poller/poller.test.ts @@ -0,0 +1,78 @@ +import { assertEquals } from "@std/assert"; +import type { NbEvent } from "../netbird/types.ts"; +import { processEnrollmentEvents } from "./poller.ts"; + +/** Helper: build an NbEvent with sensible defaults, overridable per-field. */ +function makeEvent(overrides: Partial = {}): NbEvent { + return { + id: 1, + timestamp: "2025-06-01T12:00:00Z", + activity: "peer setup key added", + activity_code: "peer.setupkey.add", + initiator_id: "init-1", + initiator_name: "admin", + target_id: "peer-1", + meta: { setup_key: "drone-key", name: "drone-01" }, + ...overrides, + }; +} + +Deno.test("processEnrollmentEvents detects peer.setupkey.add", () => { + const events: NbEvent[] = [ + makeEvent(), // enrollment — should match + makeEvent({ + id: 2, + activity_code: "peer.login", + target_id: "peer-2", + }), // not an enrollment + makeEvent({ + id: 3, + activity_code: "group.add", + target_id: "group-1", + }), // unrelated event + ]; + + const known = new Set(["drone-key"]); + const result = processEnrollmentEvents(events, known, null); + + assertEquals(result.length, 1); + assertEquals(result[0].setupKeyName, "drone-key"); + assertEquals(result[0].peerId, "peer-1"); + assertEquals(result[0].peerHostname, "drone-01"); + assertEquals(result[0].timestamp, "2025-06-01T12:00:00Z"); +}); + +Deno.test("processEnrollmentEvents filters by lastTimestamp", () => { + const events: NbEvent[] = [ + makeEvent({ id: 1, timestamp: "2025-06-01T10:00:00Z", target_id: "p1" }), + makeEvent({ id: 2, timestamp: "2025-06-01T12:00:00Z", target_id: "p2" }), + makeEvent({ id: 3, timestamp: "2025-06-01T14:00:00Z", target_id: "p3" }), + ]; + + const known = new Set(["drone-key"]); + + // Only events strictly after the watermark should pass. + const result = processEnrollmentEvents( + events, + known, + "2025-06-01T12:00:00Z", + ); + + assertEquals(result.length, 1); + assertEquals(result[0].peerId, "p3"); + assertEquals(result[0].timestamp, "2025-06-01T14:00:00Z"); +}); + +Deno.test("processEnrollmentEvents ignores unknown keys", () => { + const events: NbEvent[] = [ + makeEvent({ + meta: { setup_key: "rogue-key", name: "rogue-host" }, + target_id: "peer-x", + }), + ]; + + const known = new Set(["drone-key", "gcs-key"]); + const result = processEnrollmentEvents(events, known, null); + + assertEquals(result.length, 0); +}); diff --git a/src/poller/poller.ts b/src/poller/poller.ts new file mode 100644 index 0000000..639577d --- /dev/null +++ b/src/poller/poller.ts @@ -0,0 +1,40 @@ +import type { NbEvent } from "../netbird/types.ts"; + +export interface EnrollmentDetection { + setupKeyName: string; + peerId: string; + peerHostname: string; + timestamp: string; +} + +/** + * Filters enrollment events from the full event list. + * Returns enrollments for peers that enrolled using a known setup key + * and whose timestamp is after lastTimestamp (if provided). + */ +export function processEnrollmentEvents( + events: NbEvent[], + knownKeyNames: Set, + lastTimestamp: string | null, +): EnrollmentDetection[] { + return events + .filter((e) => { + if (e.activity_code !== "peer.setupkey.add") return false; + if (lastTimestamp && e.timestamp <= lastTimestamp) return false; + if (!knownKeyNames.has(e.meta.setup_key)) { + console.log(JSON.stringify({ + msg: "unknown_enrollment", + setup_key: e.meta.setup_key, + peer_id: e.target_id, + })); + return false; + } + return true; + }) + .map((e) => ({ + setupKeyName: e.meta.setup_key, + peerId: e.target_id, + peerHostname: e.meta.name, + timestamp: e.timestamp, + })); +}