homeio
PublicForked from doctor-io/homeio
useNetworkActions.ts
"use client";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import type { NetworkStatus } from "@/lib/shared/contracts/network";
import { queryKeys } from "@/lib/shared/query-keys";
type NetworkMutationResponse = {
data: NetworkStatus;
};
type ErrorResponsePayload = {
error?: string;
code?: string;
};
function toNetworkActionErrorMessage(
endpoint: string,
status: number,
payload: ErrorResponsePayload | null,
) {
const baseMessage = payload?.error?.trim() || `Network action failed (${status}) for ${endpoint}`;
const code = payload?.code?.trim();
if (!code) return baseMessage;
if (code === "helper_unavailable") {
return "DBus helper unavailable. Check home-server-dbus service status.";
}
if (code === "network_manager_unavailable") {
return "NetworkManager is unavailable on this server.";
}
if (code === "auth_failed") {
return "Wi-Fi authentication failed. Check the network password.";
}
return `${baseMessage} [${code}]`;
}
async function parseErrorPayload(response: Response) {
try {
return (await response.json()) as ErrorResponsePayload;
} catch {
return null;
}
}
async function postNetworkAction(
endpoint: string,
payload: Record<string, unknown>,
) {
const response = await fetch(endpoint, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
});
if (!response.ok) {
const errorPayload = await parseErrorPayload(response);
throw new Error(
toNetworkActionErrorMessage(endpoint, response.status, errorPayload),
);
}
const json = (await response.json()) as NetworkMutationResponse;
return json.data;
}
export function useNetworkActions() {
const queryClient = useQueryClient();
const connectMutation = useMutation({
mutationFn: (input: { ssid: string; password?: string }) =>
postNetworkAction(
"/api/v1/network/connect",
{
ssid: input.ssid,
password: input.password,
},
),
onSuccess: () => {
void Promise.all([
queryClient.invalidateQueries({ queryKey: queryKeys.networkStatus }),
queryClient.invalidateQueries({ queryKey: queryKeys.networkNetworks }),
]);
},
});
const disconnectMutation = useMutation({
mutationFn: (input?: { iface?: string }) =>
postNetworkAction(
"/api/v1/network/disconnect",
input ?? {},
),
onSuccess: () => {
void Promise.all([
queryClient.invalidateQueries({ queryKey: queryKeys.networkStatus }),
queryClient.invalidateQueries({ queryKey: queryKeys.networkNetworks }),
]);
},
});
return {
connectNetwork: connectMutation.mutateAsync,
disconnectNetwork: disconnectMutation.mutateAsync,
isConnecting: connectMutation.isPending,
isDisconnecting: disconnectMutation.isPending,
actionError: connectMutation.error ?? disconnectMutation.error ?? null,
};
}