mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-01-29 17:15:38 +08:00
feat: retry subscription fetch using Clash proxy on failure
This commit is contained in:
@@ -25,6 +25,7 @@
|
|||||||
- 代理组显示节点数量
|
- 代理组显示节点数量
|
||||||
- 统一运行模式检测,支持管理员模式下开启TUN模式
|
- 统一运行模式检测,支持管理员模式下开启TUN模式
|
||||||
- 托盘切换代理模式会根据设置自动断开之前连接
|
- 托盘切换代理模式会根据设置自动断开之前连接
|
||||||
|
- 如订阅获取失败回退使用Clash内核代理再次尝试
|
||||||
|
|
||||||
#### 移除了:
|
#### 移除了:
|
||||||
- 实时保存窗口位置和大小。这个功能可能会导致窗口异常大小和位置,还需观察。
|
- 实时保存窗口位置和大小。这个功能可能会导致窗口异常大小和位置,还需观察。
|
||||||
|
|||||||
@@ -179,17 +179,21 @@ export const ProfileItem = (props: Props) => {
|
|||||||
/// 0 不使用任何代理
|
/// 0 不使用任何代理
|
||||||
/// 1 使用订阅好的代理
|
/// 1 使用订阅好的代理
|
||||||
/// 2 至少使用一个代理,根据订阅,如果没订阅,默认使用系统代理
|
/// 2 至少使用一个代理,根据订阅,如果没订阅,默认使用系统代理
|
||||||
const onUpdate = useLockFn(async (type: 0 | 1 | 2) => {
|
const onUpdate = useLockFn(async (type: 0 | 1 | 2): Promise<void> => {
|
||||||
setAnchorEl(null);
|
setAnchorEl(null);
|
||||||
setLoadingCache((cache) => ({ ...cache, [itemData.uid]: true }));
|
setLoadingCache((cache) => ({ ...cache, [itemData.uid]: true }));
|
||||||
|
|
||||||
const option: Partial<IProfileOption> = {};
|
// 存储原始设置以便回退后恢复
|
||||||
|
const originalOptions = {
|
||||||
|
with_proxy: itemData.option?.with_proxy,
|
||||||
|
self_proxy: itemData.option?.self_proxy
|
||||||
|
};
|
||||||
|
|
||||||
|
// 根据类型设置初始更新选项
|
||||||
|
const option: Partial<IProfileOption> = {};
|
||||||
if (type === 0) {
|
if (type === 0) {
|
||||||
option.with_proxy = false;
|
option.with_proxy = false;
|
||||||
option.self_proxy = false;
|
option.self_proxy = false;
|
||||||
} else if (type === 1) {
|
|
||||||
// nothing
|
|
||||||
} else if (type === 2) {
|
} else if (type === 2) {
|
||||||
if (itemData.option?.self_proxy) {
|
if (itemData.option?.self_proxy) {
|
||||||
option.with_proxy = false;
|
option.with_proxy = false;
|
||||||
@@ -201,14 +205,31 @@ export const ProfileItem = (props: Props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// 尝试正常更新
|
||||||
await updateProfile(itemData.uid, option);
|
await updateProfile(itemData.uid, option);
|
||||||
Notice.success(t("Update subscription successfully"));
|
Notice.success(t("Update subscription successfully"));
|
||||||
mutate("getProfiles");
|
mutate("getProfiles");
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
|
// 更新失败,尝试使用自身代理
|
||||||
const errmsg = err?.message || err.toString();
|
const errmsg = err?.message || err.toString();
|
||||||
Notice.error(
|
Notice.info(t("Update failed, retrying with Clash proxy..."));
|
||||||
errmsg.replace(/error sending request for url (\S+?): /, ""),
|
|
||||||
);
|
try {
|
||||||
|
await updateProfile(itemData.uid, {
|
||||||
|
with_proxy: false,
|
||||||
|
self_proxy: true
|
||||||
|
});
|
||||||
|
|
||||||
|
Notice.success(t("Update with Clash proxy successfully"));
|
||||||
|
|
||||||
|
await updateProfile(itemData.uid, originalOptions);
|
||||||
|
mutate("getProfiles");
|
||||||
|
} catch (retryErr: any) {
|
||||||
|
const retryErrmsg = retryErr?.message || retryErr.toString();
|
||||||
|
Notice.error(
|
||||||
|
`${t("Update failed even with Clash proxy")}: ${retryErrmsg.replace(/error sending request for url (\S+?): /, "")}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setLoadingCache((cache) => ({ ...cache, [itemData.uid]: false }));
|
setLoadingCache((cache) => ({ ...cache, [itemData.uid]: false }));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,11 +88,13 @@ export const ProfileViewer = forwardRef<ProfileViewerRef, Props>(
|
|||||||
formIns.handleSubmit(async (form) => {
|
formIns.handleSubmit(async (form) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
|
// 基本验证
|
||||||
if (!form.type) throw new Error("`Type` should not be null");
|
if (!form.type) throw new Error("`Type` should not be null");
|
||||||
if (form.type === "remote" && !form.url) {
|
if (form.type === "remote" && !form.url) {
|
||||||
throw new Error("The URL should not be null");
|
throw new Error("The URL should not be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 处理表单数据
|
||||||
if (form.option?.update_interval) {
|
if (form.option?.update_interval) {
|
||||||
form.option.update_interval = +form.option.update_interval;
|
form.option.update_interval = +form.option.update_interval;
|
||||||
} else {
|
} else {
|
||||||
@@ -101,25 +103,72 @@ export const ProfileViewer = forwardRef<ProfileViewerRef, Props>(
|
|||||||
if (form.option?.user_agent === "") {
|
if (form.option?.user_agent === "") {
|
||||||
delete form.option.user_agent;
|
delete form.option.user_agent;
|
||||||
}
|
}
|
||||||
|
|
||||||
const name = form.name || `${form.type} file`;
|
const name = form.name || `${form.type} file`;
|
||||||
const item = { ...form, name };
|
const item = { ...form, name };
|
||||||
|
const isRemote = form.type === "remote";
|
||||||
|
|
||||||
// 创建
|
// 保存原始代理设置以便回退成功后恢复
|
||||||
if (openType === "new") {
|
const originalOptions = {
|
||||||
await createProfile(item, fileDataRef.current);
|
with_proxy: form.option?.with_proxy,
|
||||||
}
|
self_proxy: form.option?.self_proxy
|
||||||
// 编辑
|
};
|
||||||
else {
|
|
||||||
if (!form.uid) throw new Error("UID not found");
|
// 执行创建或更新操作,本地配置不需要回退机制
|
||||||
await patchProfile(form.uid, item);
|
if (!isRemote) {
|
||||||
|
if (openType === "new") {
|
||||||
|
await createProfile(item, fileDataRef.current);
|
||||||
|
} else {
|
||||||
|
if (!form.uid) throw new Error("UID not found");
|
||||||
|
await patchProfile(form.uid, item);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 远程配置使用回退机制
|
||||||
|
try {
|
||||||
|
// 尝试正常操作
|
||||||
|
if (openType === "new") {
|
||||||
|
await createProfile(item, fileDataRef.current);
|
||||||
|
} else {
|
||||||
|
if (!form.uid) throw new Error("UID not found");
|
||||||
|
await patchProfile(form.uid, item);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// 首次创建/更新失败,尝试使用自身代理
|
||||||
|
Notice.info(t("Profile creation failed, retrying with Clash proxy..."));
|
||||||
|
|
||||||
|
// 使用自身代理的配置
|
||||||
|
const retryItem = {
|
||||||
|
...item,
|
||||||
|
option: {
|
||||||
|
...item.option,
|
||||||
|
with_proxy: false,
|
||||||
|
self_proxy: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 使用自身代理再次尝试
|
||||||
|
if (openType === "new") {
|
||||||
|
await createProfile(retryItem, fileDataRef.current);
|
||||||
|
} else {
|
||||||
|
if (!form.uid) throw new Error("UID not found");
|
||||||
|
await patchProfile(form.uid, retryItem);
|
||||||
|
|
||||||
|
// 编辑模式下恢复原始代理设置
|
||||||
|
await patchProfile(form.uid, { option: originalOptions });
|
||||||
|
}
|
||||||
|
|
||||||
|
Notice.success(t("Profile creation succeeded with Clash proxy"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 成功后的操作
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
setLoading(false);
|
|
||||||
setTimeout(() => formIns.reset(), 500);
|
setTimeout(() => formIns.reset(), 500);
|
||||||
fileDataRef.current = null;
|
fileDataRef.current = null;
|
||||||
props.onChange();
|
props.onChange();
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
Notice.error(err.message || err.toString());
|
Notice.error(err.message || err.toString());
|
||||||
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -560,6 +560,14 @@
|
|||||||
"Last Check Update": "Last Check Update",
|
"Last Check Update": "Last Check Update",
|
||||||
"Click to import subscription": "Click to import subscription",
|
"Click to import subscription": "Click to import subscription",
|
||||||
"Update subscription successfully": "Update subscription successfully",
|
"Update subscription successfully": "Update subscription successfully",
|
||||||
|
"Update failed, retrying with Clash proxy...": "Update failed, retrying with Clash proxy...",
|
||||||
|
"Update with Clash proxy successfully": "Update with Clash proxy successfully",
|
||||||
|
"Update failed even with Clash proxy": "Update failed even with Clash proxy",
|
||||||
|
"Profile creation failed, retrying with Clash proxy...": "Profile creation failed, retrying with Clash proxy...",
|
||||||
|
"Profile creation succeeded with Clash proxy": "Profile creation succeeded with Clash proxy",
|
||||||
|
"Import failed, retrying with Clash proxy...": "Import failed, retrying with Clash proxy...",
|
||||||
|
"Profile Imported with Clash proxy": "Profile Imported with Clash proxy",
|
||||||
|
"Import failed even with Clash proxy": "Import failed even with Clash proxy",
|
||||||
"Current Node": "Current Node",
|
"Current Node": "Current Node",
|
||||||
"No active proxy node": "No active proxy node",
|
"No active proxy node": "No active proxy node",
|
||||||
"Network Settings": "Network Settings",
|
"Network Settings": "Network Settings",
|
||||||
|
|||||||
@@ -560,6 +560,14 @@
|
|||||||
"Last Check Update": "最后检查更新",
|
"Last Check Update": "最后检查更新",
|
||||||
"Click to import subscription": "点击导入订阅",
|
"Click to import subscription": "点击导入订阅",
|
||||||
"Update subscription successfully": "订阅更新成功",
|
"Update subscription successfully": "订阅更新成功",
|
||||||
|
"Update failed, retrying with Clash proxy...": "订阅更新失败,尝试使用 Clash 代理更新",
|
||||||
|
"Update with Clash proxy successfully": "使用 Clash 代理更新成功",
|
||||||
|
"Update failed even with Clash proxy": "使用 Clash 代理更新也失败",
|
||||||
|
"Profile creation failed, retrying with Clash proxy...": "订阅创建失败,尝试使用 Clash 代理创建",
|
||||||
|
"Profile creation succeeded with Clash proxy": "使用 Clash 代理创建订阅成功",
|
||||||
|
"Import failed, retrying with Clash proxy...": "订阅导入失败,尝试使用 Clash 代理导入",
|
||||||
|
"Profile Imported with Clash proxy": "使用 Clash 代理导入订阅成功",
|
||||||
|
"Import failed even with Clash proxy": "使用 Clash 代理导入订阅也失败",
|
||||||
"Current Node": "当前节点",
|
"Current Node": "当前节点",
|
||||||
"No active proxy node": "暂无激活的代理节点",
|
"No active proxy node": "暂无激活的代理节点",
|
||||||
"Network Settings": "网络设置",
|
"Network Settings": "网络设置",
|
||||||
|
|||||||
@@ -142,15 +142,36 @@ const ProfilePage = () => {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// 尝试正常导入
|
||||||
await importProfile(url);
|
await importProfile(url);
|
||||||
Notice.success(t("Profile Imported Successfully"));
|
Notice.success(t("Profile Imported Successfully"));
|
||||||
setUrl("");
|
setUrl("");
|
||||||
setLoading(false);
|
|
||||||
mutateProfiles();
|
mutateProfiles();
|
||||||
await onEnhance(false);
|
await onEnhance(false);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
Notice.error(err.message || err.toString());
|
// 首次导入失败,尝试使用自身代理
|
||||||
setLoading(false);
|
const errmsg = err.message || err.toString();
|
||||||
|
Notice.info(t("Import failed, retrying with Clash proxy..."));
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 使用自身代理尝试导入
|
||||||
|
await importProfile(url, {
|
||||||
|
with_proxy: false,
|
||||||
|
self_proxy: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// 回退导入成功
|
||||||
|
Notice.success(t("Profile Imported with Clash proxy"));
|
||||||
|
setUrl("");
|
||||||
|
mutateProfiles();
|
||||||
|
await onEnhance(false);
|
||||||
|
} catch (retryErr: any) {
|
||||||
|
// 回退导入也失败
|
||||||
|
const retryErrmsg = retryErr?.message || retryErr.toString();
|
||||||
|
Notice.error(
|
||||||
|
`${t("Import failed even with Clash proxy")}: ${retryErrmsg}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setDisabled(false);
|
setDisabled(false);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|||||||
@@ -36,10 +36,10 @@ export async function saveProfileFile(index: string, fileData: string) {
|
|||||||
return invoke<void>("save_profile_file", { index, fileData });
|
return invoke<void>("save_profile_file", { index, fileData });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function importProfile(url: string) {
|
export async function importProfile(url: string, option?: IProfileOption) {
|
||||||
return invoke<void>("import_profile", {
|
return invoke<void>("import_profile", {
|
||||||
url,
|
url,
|
||||||
option: { with_proxy: true },
|
option: option || { with_proxy: true },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user