mirror of https://github.com/jetkvm/kvm.git
feat(ui): add network settings tab
This commit is contained in:
parent
d9eae340bf
commit
fd3a8cb553
|
@ -143,3 +143,7 @@ func (c *DHCPClient) loadLeaseFile() error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *DHCPClient) GetLease() *Lease {
|
||||||
|
return c.lease
|
||||||
|
}
|
||||||
|
|
|
@ -960,6 +960,8 @@ var rpcHandlers = map[string]RPCHandler{
|
||||||
"getDeviceID": {Func: rpcGetDeviceID},
|
"getDeviceID": {Func: rpcGetDeviceID},
|
||||||
"deregisterDevice": {Func: rpcDeregisterDevice},
|
"deregisterDevice": {Func: rpcDeregisterDevice},
|
||||||
"getCloudState": {Func: rpcGetCloudState},
|
"getCloudState": {Func: rpcGetCloudState},
|
||||||
|
"getNetworkState": {Func: rpcGetNetworkState},
|
||||||
|
"renewDHCPLease": {Func: rpcRenewDHCPLease},
|
||||||
"keyboardReport": {Func: rpcKeyboardReport, Params: []string{"modifier", "keys"}},
|
"keyboardReport": {Func: rpcKeyboardReport, Params: []string{"modifier", "keys"}},
|
||||||
"absMouseReport": {Func: rpcAbsMouseReport, Params: []string{"x", "y", "buttons"}},
|
"absMouseReport": {Func: rpcAbsMouseReport, Params: []string{"x", "y", "buttons"}},
|
||||||
"relMouseReport": {Func: rpcRelMouseReport, Params: []string{"dx", "dy", "buttons"}},
|
"relMouseReport": {Func: rpcRelMouseReport, Params: []string{"dx", "dy", "buttons"}},
|
||||||
|
|
29
network.go
29
network.go
|
@ -47,6 +47,14 @@ type NetworkInterfaceState struct {
|
||||||
checked bool
|
checked bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RpcNetworkState struct {
|
||||||
|
InterfaceName string `json:"interface_name"`
|
||||||
|
MacAddress string `json:"mac_address"`
|
||||||
|
IPv4 string `json:"ipv4,omitempty"`
|
||||||
|
IPv6 string `json:"ipv6,omitempty"`
|
||||||
|
DHCPLease *udhcpc.Lease `json:"dhcp_lease,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
func (s *NetworkInterfaceState) IsUp() bool {
|
func (s *NetworkInterfaceState) IsUp() bool {
|
||||||
return s.interfaceUp
|
return s.interfaceUp
|
||||||
}
|
}
|
||||||
|
@ -305,6 +313,27 @@ func (s *NetworkInterfaceState) HandleLinkUpdate(update netlink.LinkUpdate) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func rpcGetNetworkState() RpcNetworkState {
|
||||||
|
return RpcNetworkState{
|
||||||
|
InterfaceName: networkState.interfaceName,
|
||||||
|
MacAddress: networkState.macAddr.String(),
|
||||||
|
IPv4: networkState.ipv4Addr.String(),
|
||||||
|
IPv6: networkState.ipv6Addr.String(),
|
||||||
|
DHCPLease: networkState.dhcpClient.GetLease(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func rpcRenewDHCPLease() error {
|
||||||
|
if networkState == nil {
|
||||||
|
return fmt.Errorf("network state not initialized")
|
||||||
|
}
|
||||||
|
if networkState.dhcpClient == nil {
|
||||||
|
return fmt.Errorf("dhcp client not initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
return networkState.dhcpClient.Renew()
|
||||||
|
}
|
||||||
|
|
||||||
func initNetwork() {
|
func initNetwork() {
|
||||||
ensureConfigLoaded()
|
ensureConfigLoaded()
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ import SettingsVideoRoute from "./routes/devices.$id.settings.video";
|
||||||
import SettingsAppearanceRoute from "./routes/devices.$id.settings.appearance";
|
import SettingsAppearanceRoute from "./routes/devices.$id.settings.appearance";
|
||||||
import * as SettingsGeneralIndexRoute from "./routes/devices.$id.settings.general._index";
|
import * as SettingsGeneralIndexRoute from "./routes/devices.$id.settings.general._index";
|
||||||
import SettingsGeneralUpdateRoute from "./routes/devices.$id.settings.general.update";
|
import SettingsGeneralUpdateRoute from "./routes/devices.$id.settings.general.update";
|
||||||
|
import SettingsNetworkRoute from "./routes/devices.$id.settings.network";
|
||||||
import SecurityAccessLocalAuthRoute from "./routes/devices.$id.settings.access.local-auth";
|
import SecurityAccessLocalAuthRoute from "./routes/devices.$id.settings.access.local-auth";
|
||||||
import SettingsMacrosRoute from "./routes/devices.$id.settings.macros";
|
import SettingsMacrosRoute from "./routes/devices.$id.settings.macros";
|
||||||
import SettingsMacrosAddRoute from "./routes/devices.$id.settings.macros.add";
|
import SettingsMacrosAddRoute from "./routes/devices.$id.settings.macros.add";
|
||||||
|
@ -156,6 +157,10 @@ if (isOnDevice) {
|
||||||
path: "hardware",
|
path: "hardware",
|
||||||
element: <SettingsHardwareRoute />,
|
element: <SettingsHardwareRoute />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "network",
|
||||||
|
element: <SettingsNetworkRoute />,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "access",
|
path: "access",
|
||||||
children: [
|
children: [
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
|
import { SettingsPageHeader } from "../components/SettingsPageheader";
|
||||||
|
import { SelectMenuBasic } from "../components/SelectMenuBasic";
|
||||||
|
|
||||||
|
import { SettingsItem } from "./devices.$id.settings";
|
||||||
|
import { useJsonRpc } from "@/hooks/useJsonRpc";
|
||||||
|
import { Button } from "@components/Button";
|
||||||
|
import notifications from "@/notifications";
|
||||||
|
|
||||||
|
interface DhcpLease {
|
||||||
|
ip?: string;
|
||||||
|
netmask?: string;
|
||||||
|
broadcast?: string;
|
||||||
|
ttl?: string;
|
||||||
|
mtu?: string;
|
||||||
|
hostname?: string;
|
||||||
|
domain?: string;
|
||||||
|
bootp_next_server?: string;
|
||||||
|
bootp_server_name?: string;
|
||||||
|
bootp_file?: string;
|
||||||
|
timezone?: string;
|
||||||
|
routers?: string[];
|
||||||
|
dns?: string[];
|
||||||
|
ntp_servers?: string[];
|
||||||
|
lpr_servers?: string[];
|
||||||
|
_time_servers?: string[];
|
||||||
|
_name_servers?: string[];
|
||||||
|
_log_servers?: string[];
|
||||||
|
_cookie_servers?: string[];
|
||||||
|
_wins_servers?: string[];
|
||||||
|
_swap_server?: string;
|
||||||
|
boot_size?: string;
|
||||||
|
root_path?: string;
|
||||||
|
lease?: string;
|
||||||
|
dhcp_type?: string;
|
||||||
|
server_id?: string;
|
||||||
|
message?: string;
|
||||||
|
tftp?: string;
|
||||||
|
bootfile?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface NetworkState {
|
||||||
|
interface_name?: string;
|
||||||
|
mac_address?: string;
|
||||||
|
ipv4?: string;
|
||||||
|
ipv6?: string;
|
||||||
|
dhcp_lease?: DhcpLease;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SettingsNetworkRoute() {
|
||||||
|
const [send] = useJsonRpc();
|
||||||
|
const [networkState, setNetworkState] = useState<NetworkState | null>(null);
|
||||||
|
|
||||||
|
|
||||||
|
const getNetworkState = useCallback(() => {
|
||||||
|
send("getNetworkState", {}, resp => {
|
||||||
|
if ("error" in resp) return;
|
||||||
|
setNetworkState(resp.result as NetworkState);
|
||||||
|
});
|
||||||
|
}, [send]);
|
||||||
|
|
||||||
|
const handleRenewLease = useCallback(() => {
|
||||||
|
send("renewDHCPLease", {}, resp => {
|
||||||
|
if ("error" in resp) {
|
||||||
|
notifications.error("Failed to renew lease: " + resp.error.message);
|
||||||
|
} else {
|
||||||
|
notifications.success("DHCP lease renewed");
|
||||||
|
getNetworkState();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [send, getNetworkState]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getNetworkState();
|
||||||
|
}, [getNetworkState]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<SettingsPageHeader
|
||||||
|
title="Network"
|
||||||
|
description="Configure your network settings"
|
||||||
|
/>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<SettingsItem
|
||||||
|
title="IPv4 Address"
|
||||||
|
description={
|
||||||
|
<span className="select-text font-mono">{networkState?.ipv4}</span>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<SettingsItem
|
||||||
|
title="IPv6 Address"
|
||||||
|
description={<span className="select-text font-mono">{networkState?.ipv6}</span>}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<SettingsItem
|
||||||
|
title="MAC Address"
|
||||||
|
description={<span className="select-auto font-mono">{networkState?.mac_address}</span>}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<SettingsItem
|
||||||
|
title="DHCP Lease"
|
||||||
|
description={<>
|
||||||
|
<ul>
|
||||||
|
{networkState?.dhcp_lease?.ip && <li>IP: <strong>{networkState?.dhcp_lease?.ip}</strong></li>}
|
||||||
|
{networkState?.dhcp_lease?.netmask && <li>Subnet: <strong>{networkState?.dhcp_lease?.netmask}</strong></li>}
|
||||||
|
{networkState?.dhcp_lease?.broadcast && <li>Broadcast: <strong>{networkState?.dhcp_lease?.broadcast}</strong></li>}
|
||||||
|
{networkState?.dhcp_lease?.ttl && <li>TTL: <strong>{networkState?.dhcp_lease?.ttl}</strong></li>}
|
||||||
|
{networkState?.dhcp_lease?.mtu && <li>MTU: <strong>{networkState?.dhcp_lease?.mtu}</strong></li>}
|
||||||
|
{networkState?.dhcp_lease?.hostname && <li>Hostname: <strong>{networkState?.dhcp_lease?.hostname}</strong></li>}
|
||||||
|
{networkState?.dhcp_lease?.domain && <li>Domain: <strong>{networkState?.dhcp_lease?.domain}</strong></li>}
|
||||||
|
{networkState?.dhcp_lease?.routers && <li>Gateway: <strong>{networkState?.dhcp_lease?.routers.join(", ")}</strong></li>}
|
||||||
|
{networkState?.dhcp_lease?.dns && <li>DNS: <strong>{networkState?.dhcp_lease?.dns.join(", ")}</strong></li>}
|
||||||
|
{networkState?.dhcp_lease?.ntp_servers && <li>NTP Servers: <strong>{networkState?.dhcp_lease?.ntp_servers.join(", ")}</strong></li>}
|
||||||
|
</ul>
|
||||||
|
</>}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
size="SM"
|
||||||
|
theme="light"
|
||||||
|
text="Renew lease"
|
||||||
|
onClick={() => {
|
||||||
|
handleRenewLease();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</SettingsItem>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ import {
|
||||||
LuArrowLeft,
|
LuArrowLeft,
|
||||||
LuPalette,
|
LuPalette,
|
||||||
LuCommand,
|
LuCommand,
|
||||||
|
LuNetwork,
|
||||||
} from "react-icons/lu";
|
} from "react-icons/lu";
|
||||||
import React, { useEffect, useRef, useState } from "react";
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
|
@ -207,6 +208,17 @@ export default function SettingsRoute() {
|
||||||
</div>
|
</div>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="shrink-0">
|
||||||
|
<NavLink
|
||||||
|
to="network"
|
||||||
|
className={({ isActive }) => (isActive ? "active" : "")}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-x-2 rounded-md px-2.5 py-2.5 text-sm transition-colors hover:bg-slate-100 dark:hover:bg-slate-700 [.active_&]:bg-blue-50 [.active_&]:!text-blue-700 md:[.active_&]:bg-transparent dark:[.active_&]:bg-blue-900 dark:[.active_&]:!text-blue-200 dark:md:[.active_&]:bg-transparent">
|
||||||
|
<LuNetwork className="h-4 w-4 shrink-0" />
|
||||||
|
<h1>Network</h1>
|
||||||
|
</div>
|
||||||
|
</NavLink>
|
||||||
|
</div>
|
||||||
<div className="shrink-0">
|
<div className="shrink-0">
|
||||||
<NavLink
|
<NavLink
|
||||||
to="advanced"
|
to="advanced"
|
||||||
|
|
Loading…
Reference in New Issue