refactor: simplify API routes and add sniffer status functionality
Some checks failed
Build and Deploy MITM Webserver / build (push) Failing after 5s
Some checks failed
Build and Deploy MITM Webserver / build (push) Failing after 5s
- Updated API routes to remove redundant prefixes. - Introduced sniffer status fetching in the backend API. - Added Sniffing page and integrated it into the routing. - Created a new hook for backend API interactions. - Implemented sniffer status type definitions.
This commit is contained in:
256
frontend/src/hooks/useBackendAPI.ts
Normal file
256
frontend/src/hooks/useBackendAPI.ts
Normal file
@@ -0,0 +1,256 @@
|
||||
// src/hooks/useNetwork.ts
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { useCallback, useState } from "react";
|
||||
import * as api from "../api/apiClient";
|
||||
import type {
|
||||
BridgeCreateRequest,
|
||||
BridgeInfo,
|
||||
BridgeRemoveRequest,
|
||||
FullState,
|
||||
InterfaceInfo,
|
||||
RouteInfo,
|
||||
} from "../types/network";
|
||||
import { SnifferStatusResponse } from "../types/sniffer";
|
||||
|
||||
/**
|
||||
* useNetwork
|
||||
*
|
||||
* - queries start disabled (no automatic network calls)
|
||||
* - calling fetchInterfaces()/fetchBridges()/... will:
|
||||
* 1) fetch and cache the data right away (queryClient.fetchQuery)
|
||||
* 2) enable the corresponding useQuery so it becomes "active" and will
|
||||
* auto-refetch based on the query options
|
||||
*
|
||||
* This gives "no initial auto-fetch" but "once fetched, auto-updates".
|
||||
*/
|
||||
export function useBackendAPI() {
|
||||
const qc = useQueryClient();
|
||||
|
||||
// per-query enabled flags (start false => no automatic fetch)
|
||||
const [interfacesEnabled, setInterfacesEnabled] = useState(false);
|
||||
const [linksEnabled, setLinksEnabled] = useState(false);
|
||||
const [routesEnabled, setRoutesEnabled] = useState(false);
|
||||
const [bridgesEnabled, setBridgesEnabled] = useState(false);
|
||||
const [fullStateEnabled, setFullStateEnabled] = useState(false);
|
||||
|
||||
// common query options once enabled
|
||||
const commonOptions = {
|
||||
refetchOnWindowFocus: true,
|
||||
staleTime: 1000 * 10, // 10s
|
||||
};
|
||||
|
||||
// Queries (disabled initially)
|
||||
const interfacesQuery = useQuery<InterfaceInfo[]>({
|
||||
queryKey: ["interfaces"],
|
||||
queryFn: api.fetchInterfaces,
|
||||
enabled: interfacesEnabled,
|
||||
...commonOptions,
|
||||
});
|
||||
|
||||
const linksQuery = useQuery<InterfaceInfo[]>({
|
||||
queryKey: ["links"],
|
||||
queryFn: api.fetchLinks,
|
||||
enabled: linksEnabled,
|
||||
...commonOptions,
|
||||
});
|
||||
|
||||
const routesQuery = useQuery<RouteInfo[]>({
|
||||
queryKey: ["routes"],
|
||||
queryFn: api.fetchRoutes,
|
||||
enabled: routesEnabled,
|
||||
...commonOptions,
|
||||
});
|
||||
|
||||
const bridgesQuery = useQuery<BridgeInfo[]>({
|
||||
queryKey: ["bridges"],
|
||||
queryFn: api.fetchBridges,
|
||||
enabled: bridgesEnabled,
|
||||
...commonOptions,
|
||||
});
|
||||
|
||||
const fullStateQuery = useQuery<FullState>({
|
||||
queryKey: ["full-state"],
|
||||
queryFn: api.fetchFullState,
|
||||
enabled: fullStateEnabled,
|
||||
...commonOptions,
|
||||
});
|
||||
|
||||
const snifferStatusQuery = useQuery<SnifferStatusResponse>({
|
||||
queryKey: ["sniffer-status"],
|
||||
queryFn: api.fetchSnifferStatus,
|
||||
enabled: fullStateEnabled,
|
||||
...commonOptions,
|
||||
});
|
||||
|
||||
// Imperative fetch helpers that also enable auto-refetch behavior
|
||||
const fetchInterfaces = useCallback(async () => {
|
||||
const res = await qc.fetchQuery<InterfaceInfo[]>({
|
||||
queryKey: ["interfaces"],
|
||||
queryFn: api.fetchInterfaces,
|
||||
staleTime: 1000 * 10
|
||||
});
|
||||
setInterfacesEnabled(true);
|
||||
return res;
|
||||
}, [qc]);
|
||||
|
||||
const fetchLinks = useCallback(async () => {
|
||||
const res = await qc.fetchQuery<InterfaceInfo[]>({
|
||||
queryKey: ["links"],
|
||||
queryFn: api.fetchLinks,
|
||||
staleTime: 1000 * 10
|
||||
});
|
||||
setLinksEnabled(true);
|
||||
return res;
|
||||
}, [qc]);
|
||||
|
||||
const fetchRoutes = useCallback(async () => {
|
||||
const res = await qc.fetchQuery<RouteInfo[]>({
|
||||
queryKey: ["routes"],
|
||||
queryFn: api.fetchRoutes,
|
||||
staleTime: 1000 * 10
|
||||
});
|
||||
setRoutesEnabled(true);
|
||||
return res;
|
||||
}, [qc]);
|
||||
|
||||
const fetchBridges = useCallback(async () => {
|
||||
const res = await qc.fetchQuery<BridgeInfo[]>({
|
||||
queryKey: ["bridges"],
|
||||
queryFn: api.fetchBridges,
|
||||
staleTime: 1000 * 10
|
||||
});
|
||||
setBridgesEnabled(true);
|
||||
return res;
|
||||
}, [qc]);
|
||||
|
||||
const fetchFullState = useCallback(async () => {
|
||||
const res = await qc.fetchQuery<FullState>({
|
||||
queryKey: ["full-state"],
|
||||
queryFn: api.fetchFullState,
|
||||
staleTime: 1000 * 5
|
||||
});
|
||||
setFullStateEnabled(true);
|
||||
return res;
|
||||
}, [qc]);
|
||||
|
||||
const fetchSnifferStatus = useCallback(async () => {
|
||||
const res = await qc.fetchQuery<SnifferStatusResponse>({
|
||||
queryKey: ["sniffer-status"],
|
||||
queryFn: api.fetchSnifferStatus,
|
||||
staleTime: 1000 * 5
|
||||
});
|
||||
setFullStateEnabled(true);
|
||||
return res;
|
||||
}, [qc]);
|
||||
|
||||
// Local loading state for simple UI feedback
|
||||
const [isCreating, setIsCreating] = useState(false);
|
||||
const [isRemoving, setIsRemoving] = useState(false);
|
||||
|
||||
// Simple imperative functions that call the API and invalidate queries
|
||||
async function createBridge(payload: BridgeCreateRequest) {
|
||||
setIsCreating(true);
|
||||
try {
|
||||
await api.createBridge(payload);
|
||||
// If the query is enabled it will refetch automatically after invalidation.
|
||||
await qc.invalidateQueries({ queryKey: ["bridges"] });
|
||||
await qc.invalidateQueries({ queryKey: ["full-state"] });
|
||||
await qc.invalidateQueries({ queryKey: ["interfaces"] });
|
||||
} catch (err) {
|
||||
const message =
|
||||
err instanceof Error ? err.message : typeof err === "string" ? err : "Create bridge failed";
|
||||
throw new Error(message);
|
||||
} finally {
|
||||
setIsCreating(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function removeBridge(payload: BridgeRemoveRequest) {
|
||||
setIsRemoving(true);
|
||||
try {
|
||||
await api.removeBridge(payload);
|
||||
await qc.invalidateQueries({ queryKey: ["bridges"] });
|
||||
await qc.invalidateQueries({ queryKey: ["full-state"] });
|
||||
await qc.invalidateQueries({ queryKey: ["interfaces"] });
|
||||
await qc.invalidateQueries({ queryKey: ["sniffer-status"] });
|
||||
} catch (err) {
|
||||
const message =
|
||||
err instanceof Error ? err.message : typeof err === "string" ? err : "Remove bridge failed";
|
||||
throw new Error(message);
|
||||
} finally {
|
||||
setIsRemoving(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience: invalidate helpers
|
||||
function refreshInterfaces() {
|
||||
return qc.invalidateQueries({ queryKey: ["interfaces"] });
|
||||
}
|
||||
|
||||
function refreshLinks() {
|
||||
return qc.invalidateQueries({ queryKey: ["links"] });
|
||||
}
|
||||
|
||||
function refreshRoutes() {
|
||||
return qc.invalidateQueries({ queryKey: ["routes"] });
|
||||
}
|
||||
|
||||
function refreshBridges() {
|
||||
return qc.invalidateQueries({ queryKey: ["bridges"] });
|
||||
}
|
||||
|
||||
function refreshFullState() {
|
||||
return qc.invalidateQueries({ queryKey: ["full-state"] });
|
||||
}
|
||||
|
||||
function refreshSnifferStatus() {
|
||||
return qc.invalidateQueries({ queryKey: ["sniffer-status"] });
|
||||
}
|
||||
|
||||
// Convenience: refresh all queries
|
||||
function refreshAll() {
|
||||
refreshInterfaces();
|
||||
refreshLinks();
|
||||
refreshRoutes();
|
||||
refreshBridges();
|
||||
refreshFullState();
|
||||
refreshSnifferStatus();
|
||||
}
|
||||
|
||||
return {
|
||||
// queries
|
||||
interfacesQuery,
|
||||
linksQuery,
|
||||
routesQuery,
|
||||
bridgesQuery,
|
||||
fullStateQuery,
|
||||
snifferStatusQuery,
|
||||
|
||||
// manual fetchers (fetch+enable auto-updates)
|
||||
fetchInterfaces,
|
||||
fetchLinks,
|
||||
fetchRoutes,
|
||||
fetchBridges,
|
||||
fetchFullState,
|
||||
fetchSnifferStatus,
|
||||
|
||||
// simple mutation functions (imperative)
|
||||
createBridge,
|
||||
removeBridge,
|
||||
|
||||
// local loading flags
|
||||
isCreating,
|
||||
isRemoving,
|
||||
|
||||
// invalidate helpers
|
||||
refreshInterfaces,
|
||||
refreshLinks,
|
||||
refreshRoutes,
|
||||
refreshBridges,
|
||||
refreshFullState,
|
||||
refreshSnifferStatus,
|
||||
|
||||
// refresh all
|
||||
refreshAll,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user