feat: update profile with system proxy/clash proxy
This commit is contained in:
parent
a32c77c5f1
commit
90eeabae7b
@ -69,9 +69,15 @@ pub struct PrfOption {
|
|||||||
pub user_agent: Option<String>,
|
pub user_agent: Option<String>,
|
||||||
|
|
||||||
/// for `remote` profile
|
/// for `remote` profile
|
||||||
|
/// use system proxy
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub with_proxy: Option<bool>,
|
pub with_proxy: Option<bool>,
|
||||||
|
|
||||||
|
/// for `remote` profile
|
||||||
|
/// use self proxy
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub self_proxy: Option<bool>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub update_interval: Option<u64>,
|
pub update_interval: Option<u64>,
|
||||||
}
|
}
|
||||||
@ -80,9 +86,10 @@ impl PrfOption {
|
|||||||
pub fn merge(one: Option<Self>, other: Option<Self>) -> Option<Self> {
|
pub fn merge(one: Option<Self>, other: Option<Self>) -> Option<Self> {
|
||||||
match (one, other) {
|
match (one, other) {
|
||||||
(Some(mut a), Some(b)) => {
|
(Some(mut a), Some(b)) => {
|
||||||
a.user_agent = a.user_agent.or(b.user_agent);
|
a.user_agent = b.user_agent.or(a.user_agent);
|
||||||
a.with_proxy = a.with_proxy.or(b.with_proxy);
|
a.with_proxy = b.with_proxy.or(a.with_proxy);
|
||||||
a.update_interval = a.update_interval.or(b.update_interval);
|
a.self_proxy = b.self_proxy.or(a.self_proxy);
|
||||||
|
a.update_interval = b.update_interval.or(a.update_interval);
|
||||||
Some(a)
|
Some(a)
|
||||||
}
|
}
|
||||||
t @ _ => t.0.or(t.1),
|
t @ _ => t.0.or(t.1),
|
||||||
@ -174,19 +181,31 @@ impl PrfItem {
|
|||||||
desc: Option<String>,
|
desc: Option<String>,
|
||||||
option: Option<PrfOption>,
|
option: Option<PrfOption>,
|
||||||
) -> Result<PrfItem> {
|
) -> Result<PrfItem> {
|
||||||
let with_proxy = match option.as_ref() {
|
let opt_ref = option.as_ref();
|
||||||
Some(opt) => opt.with_proxy.unwrap_or(false),
|
let with_proxy = opt_ref.map_or(false, |o| o.with_proxy.unwrap_or(false));
|
||||||
None => false,
|
let self_proxy = opt_ref.map_or(false, |o| o.self_proxy.unwrap_or(false));
|
||||||
};
|
let user_agent = opt_ref.map_or(None, |o| o.user_agent.clone());
|
||||||
let user_agent = match option.as_ref() {
|
|
||||||
Some(opt) => opt.user_agent.clone(),
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut builder = reqwest::ClientBuilder::new();
|
let mut builder = reqwest::ClientBuilder::new();
|
||||||
|
|
||||||
if !with_proxy {
|
if !with_proxy && !self_proxy {
|
||||||
builder = builder.no_proxy();
|
builder = builder.no_proxy();
|
||||||
|
} else if self_proxy {
|
||||||
|
// 使用软件自己的代理
|
||||||
|
let data = super::Data::global();
|
||||||
|
let port = data.clash.lock().info.port.clone();
|
||||||
|
let port = port.ok_or(anyhow::anyhow!("failed to get clash info port"))?;
|
||||||
|
let proxy_scheme = format!("http://127.0.0.1:{port}");
|
||||||
|
|
||||||
|
if let Ok(proxy) = reqwest::Proxy::http(&proxy_scheme) {
|
||||||
|
builder = builder.proxy(proxy);
|
||||||
|
}
|
||||||
|
if let Ok(proxy) = reqwest::Proxy::https(&proxy_scheme) {
|
||||||
|
builder = builder.proxy(proxy);
|
||||||
|
}
|
||||||
|
if let Ok(proxy) = reqwest::Proxy::all(&proxy_scheme) {
|
||||||
|
builder = builder.proxy(proxy);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let version = unsafe { dirs::APP_VERSION };
|
let version = unsafe { dirs::APP_VERSION };
|
||||||
|
@ -8,7 +8,9 @@ import {
|
|||||||
DialogActions,
|
DialogActions,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
|
FormControlLabel,
|
||||||
IconButton,
|
IconButton,
|
||||||
|
Switch,
|
||||||
TextField,
|
TextField,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { Settings } from "@mui/icons-material";
|
import { Settings } from "@mui/icons-material";
|
||||||
@ -34,11 +36,15 @@ const InfoEditor = (props: Props) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (itemData) {
|
if (itemData) {
|
||||||
|
const { option } = itemData;
|
||||||
setForm({ ...itemData });
|
setForm({ ...itemData });
|
||||||
setOption(itemData.option ?? {});
|
setOption(option ?? {});
|
||||||
setShowOpt(
|
setShowOpt(
|
||||||
itemData.type === "remote" &&
|
itemData.type === "remote" &&
|
||||||
(!!itemData.option?.user_agent || !!itemData.option?.update_interval)
|
(!!option?.user_agent ||
|
||||||
|
!!option?.update_interval ||
|
||||||
|
!!option?.self_proxy ||
|
||||||
|
!!option?.with_proxy)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [itemData]);
|
}, [itemData]);
|
||||||
@ -138,6 +144,46 @@ const InfoEditor = (props: Props) => {
|
|||||||
onKeyDown={(e) => e.key === "Enter" && onUpdate()}
|
onKeyDown={(e) => e.key === "Enter" && onUpdate()}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{form.type === "remote" && showOpt && (
|
||||||
|
<FormControlLabel
|
||||||
|
label={t("Use System Proxy")}
|
||||||
|
labelPlacement="start"
|
||||||
|
sx={{ ml: 0, my: 1 }}
|
||||||
|
control={
|
||||||
|
<Switch
|
||||||
|
color="primary"
|
||||||
|
checked={option.with_proxy ?? false}
|
||||||
|
onChange={(_e, c) =>
|
||||||
|
setOption((o) => ({
|
||||||
|
self_proxy: c ? false : o.self_proxy ?? false,
|
||||||
|
with_proxy: c,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{form.type === "remote" && showOpt && (
|
||||||
|
<FormControlLabel
|
||||||
|
label={t("Use Clash Proxy")}
|
||||||
|
labelPlacement="start"
|
||||||
|
sx={{ ml: 0, my: 1 }}
|
||||||
|
control={
|
||||||
|
<Switch
|
||||||
|
color="primary"
|
||||||
|
checked={option.self_proxy ?? false}
|
||||||
|
onChange={(_e, c) =>
|
||||||
|
setOption((o) => ({
|
||||||
|
with_proxy: c ? false : o.with_proxy ?? false,
|
||||||
|
self_proxy: c,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|
||||||
<DialogActions sx={{ px: 2, pb: 2, position: "relative" }}>
|
<DialogActions sx={{ px: 2, pb: 2, position: "relative" }}>
|
||||||
|
@ -110,12 +110,32 @@ const ProfileItem = (props: Props) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const onUpdate = useLockFn(async (withProxy: boolean) => {
|
/// 0 不使用任何代理
|
||||||
|
/// 1 使用配置好的代理
|
||||||
|
/// 2 至少使用一个代理,根据配置,如果没配置,默认使用系统代理
|
||||||
|
const onUpdate = useLockFn(async (type: 0 | 1 | 2) => {
|
||||||
setAnchorEl(null);
|
setAnchorEl(null);
|
||||||
setLoadingCache((cache) => ({ ...cache, [itemData.uid]: true }));
|
setLoadingCache((cache) => ({ ...cache, [itemData.uid]: true }));
|
||||||
|
|
||||||
|
const option: Partial<CmdType.ProfileOption> = {};
|
||||||
|
|
||||||
|
if (type === 0) {
|
||||||
|
option.with_proxy = false;
|
||||||
|
option.self_proxy = false;
|
||||||
|
} else if (type === 1) {
|
||||||
|
// nothing
|
||||||
|
} else if (type === 2) {
|
||||||
|
if (itemData.option?.self_proxy) {
|
||||||
|
option.with_proxy = false;
|
||||||
|
option.self_proxy = true;
|
||||||
|
} else {
|
||||||
|
option.with_proxy = true;
|
||||||
|
option.self_proxy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await updateProfile(itemData.uid, { with_proxy: withProxy });
|
await updateProfile(itemData.uid, option);
|
||||||
mutate("getProfiles");
|
mutate("getProfiles");
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
const errmsg = err?.message || err.toString();
|
const errmsg = err?.message || err.toString();
|
||||||
@ -142,8 +162,8 @@ const ProfileItem = (props: Props) => {
|
|||||||
{ label: "Edit Info", handler: onEditInfo },
|
{ label: "Edit Info", handler: onEditInfo },
|
||||||
{ label: "Edit File", handler: onEditFile },
|
{ label: "Edit File", handler: onEditFile },
|
||||||
{ label: "Open File", handler: onOpenFile },
|
{ label: "Open File", handler: onOpenFile },
|
||||||
{ label: "Update", handler: () => onUpdate(false) },
|
{ label: "Update", handler: () => onUpdate(0) },
|
||||||
{ label: "Update(Proxy)", handler: () => onUpdate(true) },
|
{ label: "Update(Proxy)", handler: () => onUpdate(2) },
|
||||||
{ label: "Delete", handler: onDelete },
|
{ label: "Delete", handler: onDelete },
|
||||||
];
|
];
|
||||||
const fileModeMenu = [
|
const fileModeMenu = [
|
||||||
@ -199,7 +219,7 @@ const ProfileItem = (props: Props) => {
|
|||||||
disabled={loading}
|
disabled={loading}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
onUpdate(false);
|
onUpdate(1);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<RefreshRounded color="inherit" />
|
<RefreshRounded color="inherit" />
|
||||||
|
@ -9,10 +9,12 @@ import {
|
|||||||
DialogContent,
|
DialogContent,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
FormControl,
|
FormControl,
|
||||||
|
FormControlLabel,
|
||||||
IconButton,
|
IconButton,
|
||||||
InputLabel,
|
InputLabel,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
Select,
|
Select,
|
||||||
|
Switch,
|
||||||
TextField,
|
TextField,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { Settings } from "@mui/icons-material";
|
import { Settings } from "@mui/icons-material";
|
||||||
@ -40,7 +42,11 @@ const ProfileNew = (props: Props) => {
|
|||||||
|
|
||||||
const [showOpt, setShowOpt] = useState(false);
|
const [showOpt, setShowOpt] = useState(false);
|
||||||
// can add more option
|
// can add more option
|
||||||
const [option, setOption] = useSetState({ user_agent: "" });
|
const [option, setOption] = useSetState({
|
||||||
|
user_agent: "",
|
||||||
|
with_proxy: false,
|
||||||
|
self_proxy: false,
|
||||||
|
});
|
||||||
// file input
|
// file input
|
||||||
const fileDataRef = useRef<string | null>(null);
|
const fileDataRef = useRef<string | null>(null);
|
||||||
|
|
||||||
@ -141,6 +147,46 @@ const ProfileNew = (props: Props) => {
|
|||||||
onChange={(e) => setOption({ user_agent: e.target.value })}
|
onChange={(e) => setOption({ user_agent: e.target.value })}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{form.type === "remote" && showOpt && (
|
||||||
|
<FormControlLabel
|
||||||
|
label={t("Use System Proxy")}
|
||||||
|
labelPlacement="start"
|
||||||
|
sx={{ ml: 0, my: 1 }}
|
||||||
|
control={
|
||||||
|
<Switch
|
||||||
|
color="primary"
|
||||||
|
checked={option.with_proxy}
|
||||||
|
onChange={(_e, c) =>
|
||||||
|
setOption((o) => ({
|
||||||
|
self_proxy: c ? false : o.self_proxy,
|
||||||
|
with_proxy: c,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{form.type === "remote" && showOpt && (
|
||||||
|
<FormControlLabel
|
||||||
|
label={t("Use Clash Proxy")}
|
||||||
|
labelPlacement="start"
|
||||||
|
sx={{ ml: 0, my: 1 }}
|
||||||
|
control={
|
||||||
|
<Switch
|
||||||
|
color="primary"
|
||||||
|
checked={option.self_proxy}
|
||||||
|
onChange={(_e, c) =>
|
||||||
|
setOption((o) => ({
|
||||||
|
with_proxy: c ? false : o.with_proxy,
|
||||||
|
self_proxy: c,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|
||||||
<DialogActions sx={{ px: 2, pb: 2, position: "relative" }}>
|
<DialogActions sx={{ px: 2, pb: 2, position: "relative" }}>
|
||||||
|
1
src/services/types.d.ts
vendored
1
src/services/types.d.ts
vendored
@ -124,6 +124,7 @@ declare namespace CmdType {
|
|||||||
interface ProfileOption {
|
interface ProfileOption {
|
||||||
user_agent?: string;
|
user_agent?: string;
|
||||||
with_proxy?: boolean;
|
with_proxy?: boolean;
|
||||||
|
self_proxy?: boolean;
|
||||||
update_interval?: number;
|
update_interval?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user