feat: get network interface

This commit is contained in:
MystiPanda 2024-07-07 18:02:29 +08:00
parent ee56080af0
commit 1bd51be99c
No known key found for this signature in database
9 changed files with 158 additions and 110 deletions

View File

@ -322,6 +322,17 @@ pub fn copy_icon_file(path: String, name: String) -> CmdResult<String> {
} }
} }
#[tauri::command]
pub fn get_network_interfaces() -> Vec<String> {
use sysinfo::Networks;
let mut result = Vec::new();
let networks = Networks::new_with_refreshed_list();
for (interface_name, _) in &networks {
result.push(interface_name.clone());
}
return 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") {

View File

@ -51,6 +51,7 @@ fn main() -> std::io::Result<()> {
cmds::open_web_url, cmds::open_web_url,
cmds::open_core_dir, cmds::open_core_dir,
cmds::get_portable_flag, cmds::get_portable_flag,
cmds::get_network_interfaces,
// cmds::kill_sidecar, // cmds::kill_sidecar,
cmds::restart_sidecar, cmds::restart_sidecar,
// clash // clash

View File

@ -1,4 +1,4 @@
import { ReactNode, useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { useLockFn } from "ahooks"; import { useLockFn } from "ahooks";
import yaml from "js-yaml"; import yaml from "js-yaml";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@ -30,7 +30,11 @@ import {
styled, styled,
} from "@mui/material"; } from "@mui/material";
import { GroupItem } from "@/components/profile/group-item"; import { GroupItem } from "@/components/profile/group-item";
import { readProfileFile, saveProfileFile } from "@/services/cmds"; import {
getNetworkInterfaces,
readProfileFile,
saveProfileFile,
} from "@/services/cmds";
import { Notice, Switch } from "@/components/base"; import { Notice, Switch } from "@/components/base";
import getSystem from "@/utils/get-system"; import getSystem from "@/utils/get-system";
import { BaseSearchBox } from "../base/base-search-box"; import { BaseSearchBox } from "../base/base-search-box";
@ -60,7 +64,7 @@ export const GroupsEditorViewer = (props: Props) => {
const [currData, setCurrData] = useState(""); const [currData, setCurrData] = useState("");
const [visualization, setVisualization] = useState(true); const [visualization, setVisualization] = useState(true);
const [match, setMatch] = useState(() => (_: string) => true); const [match, setMatch] = useState(() => (_: string) => true);
const [interfaceNameList, setInterfaceNameList] = useState<string[]>([]);
const { control, watch, register, ...formIns } = useForm<IProxyGroupConfig>({ const { control, watch, register, ...formIns } = useForm<IProxyGroupConfig>({
defaultValues: { defaultValues: {
type: "select", type: "select",
@ -251,6 +255,10 @@ export const GroupsEditorViewer = (props: Props) => {
setProxyProviderList(Object.keys(provider)); setProxyProviderList(Object.keys(provider));
setGroupList(originGroupsObj?.["proxy-groups"] || []); setGroupList(originGroupsObj?.["proxy-groups"] || []);
}; };
const getInterfaceNameList = async () => {
let list = await getNetworkInterfaces();
setInterfaceNameList(list);
};
useEffect(() => { useEffect(() => {
fetchProxyPolicy(); fetchProxyPolicy();
}, [prependSeq, appendSeq, deleteSeq]); }, [prependSeq, appendSeq, deleteSeq]);
@ -259,6 +267,7 @@ export const GroupsEditorViewer = (props: Props) => {
fetchContent(); fetchContent();
fetchProxyPolicy(); fetchProxyPolicy();
fetchProfile(); fetchProfile();
getInterfaceNameList();
}, [open]); }, [open]);
const validateGroup = () => { const validateGroup = () => {
@ -485,11 +494,13 @@ export const GroupsEditorViewer = (props: Props) => {
render={({ field }) => ( render={({ field }) => (
<Item> <Item>
<ListItemText primary={t("Interface Name")} /> <ListItemText primary={t("Interface Name")} />
<TextField <Autocomplete
autoComplete="new-password"
size="small" size="small"
sx={{ width: "calc(100% - 150px)" }} sx={{ width: "calc(100% - 150px)" }}
{...field} options={interfaceNameList}
value={field.value}
onChange={(_, value) => value && field.onChange(value)}
renderInput={(params) => <TextField {...params} />}
/> />
</Item> </Item>
)} )}

View File

@ -475,64 +475,78 @@ export const ProfileItem = (props: Props) => {
</MenuItem> </MenuItem>
))} ))}
</Menu> </Menu>
{fileOpen && (
<EditorViewer
open={true}
initialData={readProfileFile(uid)}
language="yaml"
schema="clash"
onSave={async (prev, curr) => {
await saveProfileFile(uid, curr ?? "");
onSave && onSave(prev, curr);
}}
onClose={() => setFileOpen(false)}
/>
)}
{rulesOpen && (
<RulesEditorViewer
groupsUid={option?.groups ?? ""}
mergeUid={option?.merge ?? ""}
profileUid={uid}
property={option?.rules ?? ""}
open={true}
onSave={onSave}
onClose={() => setRulesOpen(false)}
/>
)}
{proxiesOpen && (
<ProxiesEditorViewer
profileUid={uid}
property={option?.proxies ?? ""}
open={true}
onSave={onSave}
onClose={() => setProxiesOpen(false)}
/>
)}
{groupsOpen && (
<GroupsEditorViewer
mergeUid={option?.merge ?? ""}
proxiesUid={option?.proxies ?? ""}
profileUid={uid}
property={option?.groups ?? ""}
open={true}
onSave={onSave}
onClose={() => {
setGroupsOpen(false);
}}
/>
)}
{mergeOpen && (
<EditorViewer
open={true}
initialData={readProfileFile(option?.merge ?? "")}
language="yaml"
schema="clash"
onSave={async (prev, curr) => {
await saveProfileFile(option?.merge ?? "", curr ?? "");
onSave && onSave(prev, curr);
}}
onClose={() => setMergeOpen(false)}
/>
)}
{scriptOpen && (
<EditorViewer
open={true}
initialData={readProfileFile(option?.script ?? "")}
language="javascript"
onSave={async (prev, curr) => {
await saveProfileFile(option?.script ?? "", curr ?? "");
onSave && onSave(prev, curr);
}}
onClose={() => setScriptOpen(false)}
/>
)}
<EditorViewer
open={fileOpen}
initialData={readProfileFile(uid)}
language="yaml"
schema="clash"
onSave={async (prev, curr) => {
await saveProfileFile(uid, curr ?? "");
onSave && onSave(prev, curr);
}}
onClose={() => setFileOpen(false)}
/>
<RulesEditorViewer
groupsUid={option?.groups ?? ""}
mergeUid={option?.merge ?? ""}
profileUid={uid}
property={option?.rules ?? ""}
open={rulesOpen}
onSave={onSave}
onClose={() => setRulesOpen(false)}
/>
<ProxiesEditorViewer
profileUid={uid}
property={option?.proxies ?? ""}
open={proxiesOpen}
onSave={onSave}
onClose={() => setProxiesOpen(false)}
/>
<GroupsEditorViewer
mergeUid={option?.merge ?? ""}
proxiesUid={option?.proxies ?? ""}
profileUid={uid}
property={option?.groups ?? ""}
open={groupsOpen}
onSave={onSave}
onClose={() => setGroupsOpen(false)}
/>
<EditorViewer
open={mergeOpen}
initialData={readProfileFile(option?.merge ?? "")}
language="yaml"
schema="clash"
onSave={async (prev, curr) => {
await saveProfileFile(option?.merge ?? "", curr ?? "");
onSave && onSave(prev, curr);
}}
onClose={() => setMergeOpen(false)}
/>
<EditorViewer
open={scriptOpen}
initialData={readProfileFile(option?.script ?? "")}
language="javascript"
onSave={async (prev, curr) => {
await saveProfileFile(option?.script ?? "", curr ?? "");
onSave && onSave(prev, curr);
}}
onClose={() => setScriptOpen(false)}
/>
<ConfirmViewer <ConfirmViewer
title={t("Confirm deletion")} title={t("Confirm deletion")}
message={t("This operation is not reversible")} message={t("This operation is not reversible")}

View File

@ -167,25 +167,27 @@ export const ProfileMore = (props: Props) => {
</MenuItem> </MenuItem>
))} ))}
</Menu> </Menu>
{fileOpen && (
<EditorViewer <EditorViewer
open={fileOpen} open={true}
title={`${t("Global " + id)}`} title={`${t("Global " + id)}`}
initialData={readProfileFile(id)} initialData={readProfileFile(id)}
language={id === "Merge" ? "yaml" : "javascript"} language={id === "Merge" ? "yaml" : "javascript"}
schema={id === "Merge" ? "clash" : undefined} schema={id === "Merge" ? "clash" : undefined}
onSave={async (prev, curr) => { onSave={async (prev, curr) => {
await saveProfileFile(id, curr ?? ""); await saveProfileFile(id, curr ?? "");
onSave && onSave(prev, curr); onSave && onSave(prev, curr);
}} }}
onClose={() => setFileOpen(false)} onClose={() => setFileOpen(false)}
/> />
)}
<LogViewer {logOpen && (
open={logOpen} <LogViewer
logInfo={logInfo} open={logOpen}
onClose={() => setLogOpen(false)} logInfo={logInfo}
/> onClose={() => setLogOpen(false)}
/>
)}
</> </>
); );
}; };

View File

@ -20,9 +20,10 @@ export const ConfigViewer = forwardRef<DialogRef>((_, ref) => {
close: () => setOpen(false), close: () => setOpen(false),
})); }));
if (!open) return null;
return ( return (
<EditorViewer <EditorViewer
open={open} open={true}
title={ title={
<Box> <Box>
{t("Runtime Config")} {t("Runtime Config")}

View File

@ -261,20 +261,22 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
> >
{t("Edit")} PAC {t("Edit")} PAC
</Button> </Button>
<EditorViewer {editorOpen && (
open={editorOpen} <EditorViewer
title={`${t("Edit")} PAC`} open={true}
initialData={Promise.resolve(value.pac_content ?? "")} title={`${t("Edit")} PAC`}
language="javascript" initialData={Promise.resolve(value.pac_content ?? "")}
onSave={(_prev, curr) => { language="javascript"
let pac = DEFAULT_PAC; onSave={(_prev, curr) => {
if (curr && curr.trim().length > 0) { let pac = DEFAULT_PAC;
pac = curr; if (curr && curr.trim().length > 0) {
} pac = curr;
setValue((v) => ({ ...v, pac_content: pac })); }
}} setValue((v) => ({ ...v, pac_content: pac }));
onClose={() => setEditorOpen(false)} }}
/> onClose={() => setEditorOpen(false)}
/>
)}
</ListItem> </ListItem>
</> </>
)} )}

View File

@ -123,19 +123,21 @@ export const ThemeViewer = forwardRef<DialogRef>((props, ref) => {
> >
{t("Edit")} CSS {t("Edit")} CSS
</Button> </Button>
<EditorViewer {editorOpen && (
open={editorOpen} <EditorViewer
title={`${t("Edit")} CSS`} open={true}
initialData={Promise.resolve(theme.css_injection ?? "")} title={`${t("Edit")} CSS`}
language="css" initialData={Promise.resolve(theme.css_injection ?? "")}
onSave={(_prev, curr) => { language="css"
theme.css_injection = curr; onSave={(_prev, curr) => {
handleChange("css_injection"); theme.css_injection = curr;
}} handleChange("css_injection");
onClose={() => { }}
setEditorOpen(false); onClose={() => {
}} setEditorOpen(false);
/> }}
/>
)}
</Item> </Item>
</List> </List>
</BaseDialog> </BaseDialog>

View File

@ -233,3 +233,7 @@ export async function copyIconFile(
export async function downloadIconCache(url: string, name: string) { export async function downloadIconCache(url: string, name: string) {
return invoke<string>("download_icon_cache", { url, name }); return invoke<string>("download_icon_cache", { url, name });
} }
export async function getNetworkInterfaces() {
return invoke<string[]>("get_network_interfaces");
}