file structure and comments unified
Some checks failed
Build and Deploy MITM Webserver / build (push) Failing after 3s
Some checks failed
Build and Deploy MITM Webserver / build (push) Failing after 3s
This commit is contained in:
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user