feat(nodes): add node network interface management UI

Wire up the existing node-network backend endpoints (list/create/update/
delete + apply/revert staged config) to a new NodeNetworks component on the
node detail page. Supports bridge/bond/vlan/eth interface types with
type-specific fields and surfaces IPAM linkage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-13 21:25:51 +05:00
parent 23c29dacc9
commit fbf6d174b6
6 changed files with 500 additions and 1 deletions

View File

@@ -1,6 +1,6 @@
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { apiClient } from '@/lib/api-client';
import type { ProxmoxServer, ProxmoxNode, ProxmoxServerCreate } from '@/types';
import type { ProxmoxServer, ProxmoxNode, ProxmoxServerCreate, NodeNetworkInterface } from '@/types';
import { vmKeys } from './use-instances';
export const nodeKeys = {
@@ -12,6 +12,7 @@ export const nodeKeys = {
clusterInfo: (serverId: number) => ['cluster-info', serverId] as const,
topology: ['cluster-topology'] as const,
storages: (serverId: number, node: string) => ['storages', serverId, node] as const,
networks: (serverId: number, node: string) => ['node-networks', serverId, node] as const,
};
export function useServers() {
@@ -127,3 +128,61 @@ export function useTestServerCredentials() {
apiClient.post<{ success: boolean; status?: string; message?: string }>(`/proxmox/api/servers/test`, data),
});
}
// ==================== Node network interfaces ====================
export function useNodeNetworks(serverId: number, node: string) {
return useQuery({
queryKey: nodeKeys.networks(serverId, node),
queryFn: () =>
apiClient.get<{ node: string; interfaces: NodeNetworkInterface[] }>(
`/proxmox/api/servers/${serverId}/nodes/${node}/networks`,
),
enabled: serverId > 0 && !!node,
});
}
export function useCreateNodeNetwork(serverId: number, node: string) {
const qc = useQueryClient();
return useMutation({
mutationFn: (data: Record<string, unknown>) =>
apiClient.post(`/proxmox/api/servers/${serverId}/nodes/${node}/networks`, data),
onSuccess: () => qc.invalidateQueries({ queryKey: nodeKeys.networks(serverId, node) }),
});
}
export function useUpdateNodeNetwork(serverId: number, node: string) {
const qc = useQueryClient();
return useMutation({
mutationFn: ({ iface, ...data }: { iface: string } & Record<string, unknown>) =>
apiClient.put(`/proxmox/api/servers/${serverId}/nodes/${node}/networks/${iface}`, data),
onSuccess: () => qc.invalidateQueries({ queryKey: nodeKeys.networks(serverId, node) }),
});
}
export function useDeleteNodeNetwork(serverId: number, node: string) {
const qc = useQueryClient();
return useMutation({
mutationFn: (iface: string) =>
apiClient.delete(`/proxmox/api/servers/${serverId}/nodes/${node}/networks/${iface}`),
onSuccess: () => qc.invalidateQueries({ queryKey: nodeKeys.networks(serverId, node) }),
});
}
export function useApplyNodeNetwork(serverId: number, node: string) {
const qc = useQueryClient();
return useMutation({
mutationFn: () =>
apiClient.post(`/proxmox/api/servers/${serverId}/nodes/${node}/networks/apply`, {}),
onSuccess: () => qc.invalidateQueries({ queryKey: nodeKeys.networks(serverId, node) }),
});
}
export function useRevertNodeNetwork(serverId: number, node: string) {
const qc = useQueryClient();
return useMutation({
mutationFn: () =>
apiClient.post(`/proxmox/api/servers/${serverId}/nodes/${node}/networks/revert`, {}),
onSuccess: () => qc.invalidateQueries({ queryKey: nodeKeys.networks(serverId, node) }),
});
}