file structure and comments unified
Some checks failed
Build and Deploy MITM Webserver / build (push) Failing after 3s

This commit is contained in:
2026-03-06 19:03:26 +01:00
parent 7b9a7d3a4b
commit b60a1d3118
20 changed files with 697 additions and 1823 deletions

View File

@@ -1,7 +1,7 @@
// src/hooks/useNetwork.ts
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useCallback, useState } from "react";
import * as api from "../api/apiClient";
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { useCallback, useState } from 'react';
import * as api from '../api/apiClient';
import type {
BridgeCreateRequest,
BridgeInfo,
@@ -9,24 +9,15 @@ import type {
FullState,
InterfaceInfo,
RouteInfo,
} from "../types/network";
import { SnifferStatusResponse } from "../types/sniffer";
} from '../types/network';
import { SnifferStatusResponse } from '../types/sniffer';
const TEN_SECONDS = 1000 * 10;
const FIVE_SECONDS = 1000 * 5;
/**
* 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();
const queryClient = 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);
@@ -34,132 +25,126 @@ export function useBackendAPI() {
const [fullStateEnabled, setFullStateEnabled] = useState(false);
const [snifferStatusEnabled, setSnifferStatusEnabled] = useState(false);
// common query options once enabled
const commonOptions = {
refetchOnWindowFocus: true,
staleTime: 1000 * 10, // 10s
staleTime: TEN_SECONDS,
};
// Queries (disabled initially)
const interfacesQuery = useQuery<InterfaceInfo[]>({
queryKey: ["interfaces"],
queryKey: ['interfaces'],
queryFn: api.fetchInterfaces,
enabled: interfacesEnabled,
...commonOptions,
});
const linksQuery = useQuery<InterfaceInfo[]>({
queryKey: ["links"],
queryKey: ['links'],
queryFn: api.fetchLinks,
enabled: linksEnabled,
...commonOptions,
});
const routesQuery = useQuery<RouteInfo[]>({
queryKey: ["routes"],
queryKey: ['routes'],
queryFn: api.fetchRoutes,
enabled: routesEnabled,
...commonOptions,
});
const bridgesQuery = useQuery<BridgeInfo[]>({
queryKey: ["bridges"],
queryKey: ['bridges'],
queryFn: api.fetchBridges,
enabled: bridgesEnabled,
...commonOptions,
});
const fullStateQuery = useQuery<FullState>({
queryKey: ["full-state"],
queryKey: ['full-state'],
queryFn: api.fetchFullState,
enabled: fullStateEnabled,
...commonOptions,
});
const snifferStatusQuery = useQuery<SnifferStatusResponse>({
queryKey: ["sniffer-status"],
queryKey: ['sniffer-status'],
queryFn: api.fetchSnifferStatus,
enabled: fullStateEnabled,
enabled: snifferStatusEnabled,
...commonOptions,
});
// Imperative fetch helpers that also enable auto-refetch behavior
const fetchInterfaces = useCallback(async () => {
const res = await qc.fetchQuery<InterfaceInfo[]>({
queryKey: ["interfaces"],
const result = await queryClient.fetchQuery<InterfaceInfo[]>({
queryKey: ['interfaces'],
queryFn: api.fetchInterfaces,
staleTime: 1000 * 10
staleTime: TEN_SECONDS,
});
setInterfacesEnabled(true);
return res;
}, [qc]);
return result;
}, [queryClient]);
const fetchLinks = useCallback(async () => {
const res = await qc.fetchQuery<InterfaceInfo[]>({
queryKey: ["links"],
const result = await queryClient.fetchQuery<InterfaceInfo[]>({
queryKey: ['links'],
queryFn: api.fetchLinks,
staleTime: 1000 * 10
staleTime: TEN_SECONDS,
});
setLinksEnabled(true);
return res;
}, [qc]);
return result;
}, [queryClient]);
const fetchRoutes = useCallback(async () => {
const res = await qc.fetchQuery<RouteInfo[]>({
queryKey: ["routes"],
const result = await queryClient.fetchQuery<RouteInfo[]>({
queryKey: ['routes'],
queryFn: api.fetchRoutes,
staleTime: 1000 * 10
staleTime: TEN_SECONDS,
});
setRoutesEnabled(true);
return res;
}, [qc]);
return result;
}, [queryClient]);
const fetchBridges = useCallback(async () => {
const res = await qc.fetchQuery<BridgeInfo[]>({
queryKey: ["bridges"],
const result = await queryClient.fetchQuery<BridgeInfo[]>({
queryKey: ['bridges'],
queryFn: api.fetchBridges,
staleTime: 1000 * 10
staleTime: TEN_SECONDS,
});
setBridgesEnabled(true);
return res;
}, [qc]);
return result;
}, [queryClient]);
const fetchFullState = useCallback(async () => {
const res = await qc.fetchQuery<FullState>({
queryKey: ["full-state"],
const result = await queryClient.fetchQuery<FullState>({
queryKey: ['full-state'],
queryFn: api.fetchFullState,
staleTime: 1000 * 5
staleTime: FIVE_SECONDS,
});
setFullStateEnabled(true);
return res;
}, [qc]);
return result;
}, [queryClient]);
const fetchSnifferStatus = useCallback(async () => {
const res = await qc.fetchQuery<SnifferStatusResponse>({
queryKey: ["sniffer-status"],
const result = await queryClient.fetchQuery<SnifferStatusResponse>({
queryKey: ['sniffer-status'],
queryFn: api.fetchSnifferStatus,
staleTime: 1000 * 5
staleTime: FIVE_SECONDS,
});
setFullStateEnabled(true);
return res;
}, [qc]);
setSnifferStatusEnabled(true);
return result;
}, [queryClient]);
// 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) {
await queryClient.invalidateQueries({ queryKey: ['bridges'] });
await queryClient.invalidateQueries({ queryKey: ['full-state'] });
await queryClient.invalidateQueries({ queryKey: ['interfaces'] });
} catch (error) {
const message =
err instanceof Error ? err.message : typeof err === "string" ? err : "Create bridge failed";
error instanceof Error ? error.message : typeof error === 'string' ? error : 'Create bridge failed';
throw new Error(message);
} finally {
setIsCreating(false);
@@ -170,45 +155,43 @@ export function useBackendAPI() {
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) {
await queryClient.invalidateQueries({ queryKey: ['bridges'] });
await queryClient.invalidateQueries({ queryKey: ['full-state'] });
await queryClient.invalidateQueries({ queryKey: ['interfaces'] });
await queryClient.invalidateQueries({ queryKey: ['sniffer-status'] });
} catch (error) {
const message =
err instanceof Error ? err.message : typeof err === "string" ? err : "Remove bridge failed";
error instanceof Error ? error.message : typeof error === 'string' ? error : 'Remove bridge failed';
throw new Error(message);
} finally {
setIsRemoving(false);
}
}
// Convenience: invalidate helpers
function refreshInterfaces() {
return qc.invalidateQueries({ queryKey: ["interfaces"] });
return queryClient.invalidateQueries({ queryKey: ['interfaces'] });
}
function refreshLinks() {
return qc.invalidateQueries({ queryKey: ["links"] });
return queryClient.invalidateQueries({ queryKey: ['links'] });
}
function refreshRoutes() {
return qc.invalidateQueries({ queryKey: ["routes"] });
return queryClient.invalidateQueries({ queryKey: ['routes'] });
}
function refreshBridges() {
return qc.invalidateQueries({ queryKey: ["bridges"] });
return queryClient.invalidateQueries({ queryKey: ['bridges'] });
}
function refreshFullState() {
return qc.invalidateQueries({ queryKey: ["full-state"] });
return queryClient.invalidateQueries({ queryKey: ['full-state'] });
}
function refreshSnifferStatus() {
return qc.invalidateQueries({ queryKey: ["sniffer-status"] });
return queryClient.invalidateQueries({ queryKey: ['sniffer-status'] });
}
// Convenience: refresh all queries
function refreshAll() {
refreshInterfaces();
refreshLinks();
@@ -219,39 +202,28 @@ export function useBackendAPI() {
}
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,
};
}