feat: auto-fallback to Clash proxy on scheduled subscription updates; refactor fallback logic and add request timeout
This commit is contained in:
@@ -183,12 +183,6 @@ export const ProfileItem = (props: Props) => {
|
||||
setAnchorEl(null);
|
||||
setLoadingCache((cache) => ({ ...cache, [itemData.uid]: true }));
|
||||
|
||||
// 存储原始设置以便回退后恢复
|
||||
const originalOptions = {
|
||||
with_proxy: itemData.option?.with_proxy,
|
||||
self_proxy: itemData.option?.self_proxy
|
||||
};
|
||||
|
||||
// 根据类型设置初始更新选项
|
||||
const option: Partial<IProfileOption> = {};
|
||||
if (type === 0) {
|
||||
@@ -205,31 +199,15 @@ export const ProfileItem = (props: Props) => {
|
||||
}
|
||||
|
||||
try {
|
||||
// 尝试正常更新
|
||||
// 调用后端更新(后端会自动处理回退逻辑)
|
||||
await updateProfile(itemData.uid, option);
|
||||
|
||||
// 更新成功,刷新列表
|
||||
Notice.success(t("Update subscription successfully"));
|
||||
mutate("getProfiles");
|
||||
} catch (err: any) {
|
||||
// 更新失败,尝试使用自身代理
|
||||
const errmsg = err?.message || err.toString();
|
||||
Notice.info(t("Update failed, retrying with Clash proxy..."));
|
||||
|
||||
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 {
|
||||
setLoadingCache((cache) => ({ ...cache, [itemData.uid]: false }));
|
||||
}
|
||||
|
||||
@@ -262,6 +262,25 @@ export const ProfileViewer = forwardRef<ProfileViewerRef, Props>(
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
name="option.timeout_seconds"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...text}
|
||||
{...field}
|
||||
type="number"
|
||||
placeholder="60"
|
||||
label={t("HTTP Request Timeout")}
|
||||
InputProps={{
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">{t("seconds")}</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
|
||||
@@ -60,6 +60,18 @@ const handleNoticeMessage = (
|
||||
case "set_config::error":
|
||||
Notice.error(msg);
|
||||
break;
|
||||
case "update_with_clash_proxy":
|
||||
Notice.success(`${t("Update with Clash proxy successfully")} ${msg}`);
|
||||
break;
|
||||
case "update_retry_with_clash":
|
||||
Notice.info(t("Update failed, retrying with Clash proxy..."));
|
||||
break;
|
||||
case "update_failed_even_with_clash":
|
||||
Notice.error(`${t("Update failed even with Clash proxy")}: ${msg}`);
|
||||
break;
|
||||
case "update_failed":
|
||||
Notice.error(msg);
|
||||
break;
|
||||
case "config_validate::boot_error":
|
||||
Notice.error(`${t("Boot Config Validation Failed")} ${msg}`);
|
||||
break;
|
||||
|
||||
@@ -263,6 +263,8 @@ const ProfilePage = () => {
|
||||
try {
|
||||
await updateProfile(uid);
|
||||
throttleMutate();
|
||||
} catch (err: any) {
|
||||
console.error(`更新订阅 ${uid} 失败:`, err);
|
||||
} finally {
|
||||
setLoadingCache((cache) => ({ ...cache, [uid]: false }));
|
||||
}
|
||||
|
||||
1
src/services/types.d.ts
vendored
1
src/services/types.d.ts
vendored
@@ -207,6 +207,7 @@ interface IProfileOption {
|
||||
with_proxy?: boolean;
|
||||
self_proxy?: boolean;
|
||||
update_interval?: number;
|
||||
timeout_seconds?: number;
|
||||
danger_accept_invalid_certs?: boolean;
|
||||
merge?: string;
|
||||
script?: string;
|
||||
|
||||
Reference in New Issue
Block a user