mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2024-11-15 19:22:26 +08:00
feat: display network interface
This commit is contained in:
parent
32212a46e2
commit
48f7c15035
14
src-tauri/Cargo.lock
generated
14
src-tauri/Cargo.lock
generated
|
@ -803,6 +803,7 @@ dependencies = [
|
||||||
"log 0.4.22",
|
"log 0.4.22",
|
||||||
"log4rs",
|
"log4rs",
|
||||||
"nanoid",
|
"nanoid",
|
||||||
|
"network-interface",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"open 5.2.0",
|
"open 5.2.0",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
|
@ -3243,6 +3244,19 @@ dependencies = [
|
||||||
"jni-sys",
|
"jni-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "network-interface"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "433419f898328beca4f2c6c73a1b52540658d92b0a99f0269330457e0fd998d5"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"serde",
|
||||||
|
"thiserror",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "new_debug_unreachable"
|
name = "new_debug_unreachable"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
|
|
|
@ -38,6 +38,7 @@ serde = { version = "1.0", features = ["derive"] }
|
||||||
reqwest = { version = "0.12", features = ["json", "rustls-tls"] }
|
reqwest = { version = "0.12", features = ["json", "rustls-tls"] }
|
||||||
sysproxy = { git="https://github.com/zzzgydi/sysproxy-rs", branch = "main" }
|
sysproxy = { git="https://github.com/zzzgydi/sysproxy-rs", branch = "main" }
|
||||||
tauri = { version="1", features = [ "fs-read-file", "fs-exists", "path-all", "protocol-asset", "dialog-open", "notification-all", "icon-png", "icon-ico", "clipboard-all", "global-shortcut-all", "process-all", "shell-all", "system-tray", "updater", "window-all", "devtools"] }
|
tauri = { version="1", features = [ "fs-read-file", "fs-exists", "path-all", "protocol-asset", "dialog-open", "notification-all", "icon-png", "icon-ico", "clipboard-all", "global-shortcut-all", "process-all", "shell-all", "system-tray", "updater", "window-all", "devtools"] }
|
||||||
|
network-interface = { version = "2.0.0", features = ["serde"] }
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
runas = "=1.2.0"
|
runas = "=1.2.0"
|
||||||
deelevate = "0.2.0"
|
deelevate = "0.2.0"
|
||||||
|
|
|
@ -6,6 +6,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use crate::{ret_err, wrap_err};
|
use crate::{ret_err, wrap_err};
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
use network_interface::NetworkInterface;
|
||||||
use serde_yaml::Mapping;
|
use serde_yaml::Mapping;
|
||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::{HashMap, VecDeque};
|
||||||
use sysproxy::{Autoproxy, Sysproxy};
|
use sysproxy::{Autoproxy, Sysproxy};
|
||||||
|
@ -339,6 +340,25 @@ pub fn get_network_interfaces() -> Vec<String> {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn get_network_interfaces_info() -> CmdResult<Vec<NetworkInterface>> {
|
||||||
|
use network_interface::NetworkInterface;
|
||||||
|
use network_interface::NetworkInterfaceConfig;
|
||||||
|
|
||||||
|
let names = get_network_interfaces();
|
||||||
|
let interfaces = wrap_err!(NetworkInterface::show())?;
|
||||||
|
|
||||||
|
let mut result = Vec::new();
|
||||||
|
|
||||||
|
for interface in interfaces {
|
||||||
|
if names.contains(&interface.name) {
|
||||||
|
result.push(interface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn open_devtools(app_handle: tauri::AppHandle) {
|
pub fn open_devtools(app_handle: tauri::AppHandle) {
|
||||||
if let Some(window) = app_handle.get_window("main") {
|
if let Some(window) = app_handle.get_window("main") {
|
||||||
|
|
|
@ -74,6 +74,7 @@ fn main() -> std::io::Result<()> {
|
||||||
cmds::download_icon_cache,
|
cmds::download_icon_cache,
|
||||||
cmds::open_devtools,
|
cmds::open_devtools,
|
||||||
cmds::exit_app,
|
cmds::exit_app,
|
||||||
|
cmds::get_network_interfaces_info,
|
||||||
// cmds::update_hotkeys,
|
// cmds::update_hotkeys,
|
||||||
// profile
|
// profile
|
||||||
cmds::get_profiles,
|
cmds::get_profiles,
|
||||||
|
|
135
src/components/setting/mods/network-interface-viewer.tsx
Normal file
135
src/components/setting/mods/network-interface-viewer.tsx
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { BaseDialog, DialogRef, Notice } from "@/components/base";
|
||||||
|
import { getNetworkInterfacesInfo } from "@/services/cmds";
|
||||||
|
import { alpha, Box, Button, Chip, IconButton } from "@mui/material";
|
||||||
|
import { ContentCopyRounded } from "@mui/icons-material";
|
||||||
|
import { writeText } from "@tauri-apps/api/clipboard";
|
||||||
|
|
||||||
|
export const NetworkInterfaceViewer = forwardRef<DialogRef>((props, ref) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const [networkInterfaces, setNetworkInterfaces] = useState<
|
||||||
|
INetworkInterface[]
|
||||||
|
>([]);
|
||||||
|
const [isV4, setIsV4] = useState(true);
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
open: () => {
|
||||||
|
setOpen(true);
|
||||||
|
},
|
||||||
|
close: () => setOpen(false),
|
||||||
|
}));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!open) return;
|
||||||
|
getNetworkInterfacesInfo().then((res) => {
|
||||||
|
console.log(res);
|
||||||
|
setNetworkInterfaces(res);
|
||||||
|
});
|
||||||
|
}, [open]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BaseDialog
|
||||||
|
open={open}
|
||||||
|
title={
|
||||||
|
<Box display="flex" justifyContent="space-between">
|
||||||
|
{t("Network Interface")}
|
||||||
|
<Box>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
size="small"
|
||||||
|
onClick={() => {
|
||||||
|
setIsV4((prev) => !prev);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isV4 ? t("Ipv6") : t("Ipv4")}
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
contentSx={{ width: 450, maxHeight: 330 }}
|
||||||
|
okBtn={t("Save")}
|
||||||
|
cancelBtn={t("Cancel")}
|
||||||
|
onClose={() => setOpen(false)}
|
||||||
|
onCancel={() => setOpen(false)}
|
||||||
|
>
|
||||||
|
{networkInterfaces.map((item) => (
|
||||||
|
<Box key={item.name}>
|
||||||
|
<h4>{item.name}</h4>
|
||||||
|
<Box>
|
||||||
|
{isV4 && (
|
||||||
|
<>
|
||||||
|
{item.addr.map(
|
||||||
|
(address) =>
|
||||||
|
address.V4 && (
|
||||||
|
<AddressDisplay
|
||||||
|
key={address.V4.ip}
|
||||||
|
label="Address"
|
||||||
|
content={address.V4.ip}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
<AddressDisplay label="Mac" content={item.mac_addr ?? ""} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{!isV4 && (
|
||||||
|
<>
|
||||||
|
{item.addr.map(
|
||||||
|
(address) =>
|
||||||
|
address.V6 && (
|
||||||
|
<AddressDisplay
|
||||||
|
key={address.V6.ip}
|
||||||
|
label="Address"
|
||||||
|
content={address.V6.ip}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
<AddressDisplay label="Mac" content={item.mac_addr ?? ""} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</BaseDialog>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const AddressDisplay = (props: { label: string; content: string }) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
margin: "8px 0",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box>{props.label}</Box>
|
||||||
|
<Box
|
||||||
|
sx={({ palette }) => ({
|
||||||
|
borderRadius: "8px",
|
||||||
|
padding: "2px",
|
||||||
|
background:
|
||||||
|
palette.mode === "dark"
|
||||||
|
? alpha(palette.background.paper, 0.3)
|
||||||
|
: alpha(palette.grey[400], 0.3),
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Box sx={{ display: "inline", userSelect: "text" }}>
|
||||||
|
{props.content}
|
||||||
|
</Box>
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
onClick={async () => {
|
||||||
|
await writeText(props.content);
|
||||||
|
Notice.success(t("Copy Success"));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ContentCopyRounded sx={{ fontSize: "18px" }} />
|
||||||
|
</IconButton>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
|
@ -2,7 +2,11 @@ import { useRef } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { TextField, Select, MenuItem, Typography } from "@mui/material";
|
import { TextField, Select, MenuItem, Typography } from "@mui/material";
|
||||||
|
|
||||||
import { Settings, Shuffle } from "@mui/icons-material";
|
import {
|
||||||
|
SettingsRounded,
|
||||||
|
ShuffleRounded,
|
||||||
|
LanRounded,
|
||||||
|
} from "@mui/icons-material";
|
||||||
import { DialogRef, Notice, Switch } from "@/components/base";
|
import { DialogRef, Notice, Switch } from "@/components/base";
|
||||||
import { useClash } from "@/hooks/use-clash";
|
import { useClash } from "@/hooks/use-clash";
|
||||||
import { GuardState } from "./mods/guard-state";
|
import { GuardState } from "./mods/guard-state";
|
||||||
|
@ -16,6 +20,7 @@ import getSystem from "@/utils/get-system";
|
||||||
import { useVerge } from "@/hooks/use-verge";
|
import { useVerge } from "@/hooks/use-verge";
|
||||||
import { updateGeoData } from "@/services/api";
|
import { updateGeoData } from "@/services/api";
|
||||||
import { TooltipIcon } from "@/components/base/base-tooltip-icon";
|
import { TooltipIcon } from "@/components/base/base-tooltip-icon";
|
||||||
|
import { NetworkInterfaceViewer } from "./mods/network-interface-viewer";
|
||||||
|
|
||||||
const isWIN = getSystem() === "windows";
|
const isWIN = getSystem() === "windows";
|
||||||
|
|
||||||
|
@ -37,6 +42,7 @@ const SettingClash = ({ onError }: Props) => {
|
||||||
const portRef = useRef<DialogRef>(null);
|
const portRef = useRef<DialogRef>(null);
|
||||||
const ctrlRef = useRef<DialogRef>(null);
|
const ctrlRef = useRef<DialogRef>(null);
|
||||||
const coreRef = useRef<DialogRef>(null);
|
const coreRef = useRef<DialogRef>(null);
|
||||||
|
const networkRef = useRef<DialogRef>(null);
|
||||||
|
|
||||||
const onSwitchFormat = (_e: any, value: boolean) => value;
|
const onSwitchFormat = (_e: any, value: boolean) => value;
|
||||||
const onChangeData = (patch: Partial<IConfigData>) => {
|
const onChangeData = (patch: Partial<IConfigData>) => {
|
||||||
|
@ -60,8 +66,21 @@ const SettingClash = ({ onError }: Props) => {
|
||||||
<ClashPortViewer ref={portRef} />
|
<ClashPortViewer ref={portRef} />
|
||||||
<ControllerViewer ref={ctrlRef} />
|
<ControllerViewer ref={ctrlRef} />
|
||||||
<ClashCoreViewer ref={coreRef} />
|
<ClashCoreViewer ref={coreRef} />
|
||||||
|
<NetworkInterfaceViewer ref={networkRef} />
|
||||||
|
|
||||||
<SettingItem label={t("Allow Lan")}>
|
<SettingItem
|
||||||
|
label={t("Allow Lan")}
|
||||||
|
extra={
|
||||||
|
<TooltipIcon
|
||||||
|
title={t("Network Interface")}
|
||||||
|
color={"inherit"}
|
||||||
|
icon={LanRounded}
|
||||||
|
onClick={() => {
|
||||||
|
networkRef.current?.open();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
<GuardState
|
<GuardState
|
||||||
value={allowLan ?? false}
|
value={allowLan ?? false}
|
||||||
valueProps="checked"
|
valueProps="checked"
|
||||||
|
@ -112,7 +131,7 @@ const SettingClash = ({ onError }: Props) => {
|
||||||
<TooltipIcon
|
<TooltipIcon
|
||||||
title={t("Random Port")}
|
title={t("Random Port")}
|
||||||
color={enable_random_port ? "primary" : "inherit"}
|
color={enable_random_port ? "primary" : "inherit"}
|
||||||
icon={Shuffle}
|
icon={ShuffleRounded}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
Notice.success(
|
Notice.success(
|
||||||
t("Restart Application to Apply Modifications"),
|
t("Restart Application to Apply Modifications"),
|
||||||
|
@ -148,7 +167,7 @@ const SettingClash = ({ onError }: Props) => {
|
||||||
label={t("Clash Core")}
|
label={t("Clash Core")}
|
||||||
extra={
|
extra={
|
||||||
<TooltipIcon
|
<TooltipIcon
|
||||||
icon={Settings}
|
icon={SettingsRounded}
|
||||||
onClick={() => coreRef.current?.open()}
|
onClick={() => coreRef.current?.open()}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
|
@ -241,3 +241,7 @@ export async function downloadIconCache(url: string, name: string) {
|
||||||
export async function getNetworkInterfaces() {
|
export async function getNetworkInterfaces() {
|
||||||
return invoke<string[]>("get_network_interfaces");
|
return invoke<string[]>("get_network_interfaces");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getNetworkInterfacesInfo() {
|
||||||
|
return invoke<INetworkInterface[]>("get_network_interfaces_info");
|
||||||
|
}
|
||||||
|
|
18
src/services/types.d.ts
vendored
18
src/services/types.d.ts
vendored
|
@ -197,6 +197,24 @@ interface IVergeTestItem {
|
||||||
icon?: string;
|
icon?: string;
|
||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
|
interface IAddress {
|
||||||
|
V4?: {
|
||||||
|
ip: string;
|
||||||
|
broadcast?: string;
|
||||||
|
netmask?: string;
|
||||||
|
};
|
||||||
|
V6?: {
|
||||||
|
ip: string;
|
||||||
|
broadcast?: string;
|
||||||
|
netmask?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
interface INetworkInterface {
|
||||||
|
name: string;
|
||||||
|
addr: IAddress[];
|
||||||
|
mac_addr?: string;
|
||||||
|
index: number;
|
||||||
|
}
|
||||||
|
|
||||||
interface ISeqProfileConfig {
|
interface ISeqProfileConfig {
|
||||||
prepend: [];
|
prepend: [];
|
||||||
|
|
Loading…
Reference in New Issue
Block a user