Compare commits

..

1 Commits

Author SHA1 Message Date
SaladDay 77b22e2b68 refactor: remove superseded dead code 2026-07-30 10:39:46 +00:00
9 changed files with 273 additions and 89 deletions
+3 -5
View File
@@ -29,7 +29,7 @@ import {
import { getCurrentWindow } from "@tauri-apps/api/window"; import { getCurrentWindow } from "@tauri-apps/api/window";
import type { Provider, VisibleApps } from "@/types"; import type { Provider, VisibleApps } from "@/types";
import type { EnvConflict } from "@/types/env"; import type { EnvConflict } from "@/types/env";
import { proxyKeys, useProvidersQuery, useSettingsQuery } from "@/lib/query"; import { useProvidersQuery, useSettingsQuery } from "@/lib/query";
import { import {
providersApi, providersApi,
settingsApi, settingsApi,
@@ -389,10 +389,8 @@ function App() {
await queryClient.invalidateQueries({ queryKey: ["profiles"] }); await queryClient.invalidateQueries({ queryKey: ["profiles"] });
await queryClient.invalidateQueries({ queryKey: ["mcp", "all"] }); await queryClient.invalidateQueries({ queryKey: ["mcp", "all"] });
await queryClient.invalidateQueries({ queryKey: ["skills"] }); await queryClient.invalidateQueries({ queryKey: ["skills"] });
await queryClient.invalidateQueries({ await queryClient.invalidateQueries({ queryKey: ["proxyTakeoverStatus"] });
queryKey: proxyKeys.takeoverStatus, await queryClient.invalidateQueries({ queryKey: ["proxyStatus"] });
});
await queryClient.invalidateQueries({ queryKey: proxyKeys.status });
await queryClient.invalidateQueries({ await queryClient.invalidateQueries({
queryKey: ["providers", "claude-desktop"], queryKey: ["providers", "claude-desktop"],
}); });
+2 -3
View File
@@ -15,12 +15,12 @@ import { Switch } from "@/components/ui/switch";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { ToggleRow } from "@/components/ui/toggle-row"; import { ToggleRow } from "@/components/ui/toggle-row";
import { useProxyStatus } from "@/hooks/useProxyStatus";
import { toast } from "sonner"; import { toast } from "sonner";
import { useFailoverQueue } from "@/lib/query/failover"; import { useFailoverQueue } from "@/lib/query/failover";
import { ProviderHealthBadge } from "@/components/providers/ProviderHealthBadge"; import { ProviderHealthBadge } from "@/components/providers/ProviderHealthBadge";
import { useProviderHealth } from "@/lib/query/failover"; import { useProviderHealth } from "@/lib/query/failover";
import { import {
useProxyStatusQuery,
useProxyTakeoverStatus, useProxyTakeoverStatus,
useSetProxyTakeoverForApp, useSetProxyTakeoverForApp,
useGlobalProxyConfig, useGlobalProxyConfig,
@@ -45,8 +45,7 @@ export function ProxyPanel({
isProxyPending, isProxyPending,
}: ProxyPanelProps) { }: ProxyPanelProps) {
const { t } = useTranslation(); const { t } = useTranslation();
const { data: status } = useProxyStatusQuery(); const { status, isRunning } = useProxyStatus();
const isRunning = status?.running ?? false;
// 获取应用接管状态 // 获取应用接管状态
const { data: takeoverStatus } = useProxyTakeoverStatus(); const { data: takeoverStatus } = useProxyTakeoverStatus();
+88 -19
View File
@@ -2,15 +2,15 @@
* 代理服务状态管理 Hook * 代理服务状态管理 Hook
*/ */
import { useMutation, useQueryClient } from "@tanstack/react-query"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { invoke } from "@tauri-apps/api/core";
import { toast } from "sonner"; import { toast } from "sonner";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { proxyApi } from "@/lib/api/proxy"; import type {
import { ProxyStatus,
proxyKeys, ProxyServerInfo,
useProxyStatusQuery, ProxyTakeoverStatus,
useProxyTakeoverStatus, } from "@/types/proxy";
} from "@/lib/query/proxy";
import { extractErrorMessage } from "@/utils/errorUtils"; import { extractErrorMessage } from "@/utils/errorUtils";
/** /**
@@ -21,14 +21,25 @@ export function useProxyStatus() {
const { t } = useTranslation(); const { t } = useTranslation();
// 查询状态(自动轮询) // 查询状态(自动轮询)
const { data: status } = useProxyStatusQuery(); const { data: status, isLoading } = useQuery({
queryKey: ["proxyStatus"],
queryFn: () => invoke<ProxyStatus>("get_proxy_status"),
// 仅在服务运行时轮询
refetchInterval: (query) => (query.state.data?.running ? 2000 : false),
// 保持之前的数据,避免闪烁
placeholderData: (previousData) => previousData,
});
// 查询各应用接管状态 // 查询各应用接管状态
const { data: takeoverStatus } = useProxyTakeoverStatus(false); const { data: takeoverStatus } = useQuery({
queryKey: ["proxyTakeoverStatus"],
queryFn: () => invoke<ProxyTakeoverStatus>("get_proxy_takeover_status"),
placeholderData: (previousData) => previousData,
});
// 启动服务器(总开关:仅启动服务,不接管) // 启动服务器(总开关:仅启动服务,不接管)
const startProxyServerMutation = useMutation({ const startProxyServerMutation = useMutation({
mutationFn: () => proxyApi.startProxyServer(), mutationFn: () => invoke<ProxyServerInfo>("start_proxy_server"),
onSuccess: (info) => { onSuccess: (info) => {
toast.success( toast.success(
t("proxy.server.started", { t("proxy.server.started", {
@@ -38,7 +49,7 @@ export function useProxyStatus() {
}), }),
{ closeButton: true }, { closeButton: true },
); );
queryClient.invalidateQueries({ queryKey: proxyKeys.status }); queryClient.invalidateQueries({ queryKey: ["proxyStatus"] });
}, },
onError: (error: Error) => { onError: (error: Error) => {
const detail = const detail =
@@ -55,7 +66,7 @@ export function useProxyStatus() {
// 停止服务器(仅停止服务,不改写/恢复其它应用接管状态) // 停止服务器(仅停止服务,不改写/恢复其它应用接管状态)
const stopProxyServerMutation = useMutation({ const stopProxyServerMutation = useMutation({
mutationFn: () => proxyApi.stopProxyServer(), mutationFn: () => invoke("stop_proxy_server"),
onSuccess: () => { onSuccess: () => {
toast.success( toast.success(
t("proxy.server.stopped", { t("proxy.server.stopped", {
@@ -63,7 +74,7 @@ export function useProxyStatus() {
}), }),
{ closeButton: true }, { closeButton: true },
); );
queryClient.invalidateQueries({ queryKey: proxyKeys.status }); queryClient.invalidateQueries({ queryKey: ["proxyStatus"] });
}, },
onError: (error: Error) => { onError: (error: Error) => {
const detail = const detail =
@@ -80,7 +91,7 @@ export function useProxyStatus() {
// 停止服务器(总开关关闭:强制恢复所有已接管的 Live 配置) // 停止服务器(总开关关闭:强制恢复所有已接管的 Live 配置)
const stopWithRestoreMutation = useMutation({ const stopWithRestoreMutation = useMutation({
mutationFn: () => proxyApi.stopProxyWithRestore(), mutationFn: () => invoke("stop_proxy_with_restore"),
onSuccess: () => { onSuccess: () => {
toast.success( toast.success(
t("proxy.stoppedWithRestore", { t("proxy.stoppedWithRestore", {
@@ -88,8 +99,8 @@ export function useProxyStatus() {
}), }),
{ closeButton: true }, { closeButton: true },
); );
queryClient.invalidateQueries({ queryKey: proxyKeys.status }); queryClient.invalidateQueries({ queryKey: ["proxyStatus"] });
queryClient.invalidateQueries({ queryKey: proxyKeys.takeoverStatus }); queryClient.invalidateQueries({ queryKey: ["proxyTakeoverStatus"] });
// 彻底删除所有供应商健康状态缓存(后端已清空数据库记录) // 彻底删除所有供应商健康状态缓存(后端已清空数据库记录)
queryClient.removeQueries({ queryKey: ["providerHealth"] }); queryClient.removeQueries({ queryKey: ["providerHealth"] });
// 彻底删除所有熔断器统计缓存(代理停止后熔断器状态已重置) // 彻底删除所有熔断器统计缓存(代理停止后熔断器状态已重置)
@@ -112,7 +123,7 @@ export function useProxyStatus() {
// 按应用开启/关闭接管 // 按应用开启/关闭接管
const setTakeoverForAppMutation = useMutation({ const setTakeoverForAppMutation = useMutation({
mutationFn: ({ appType, enabled }: { appType: string; enabled: boolean }) => mutationFn: ({ appType, enabled }: { appType: string; enabled: boolean }) =>
proxyApi.setProxyTakeoverForApp(appType, enabled), invoke("set_proxy_takeover_for_app", { appType, enabled }),
onSuccess: (_data, variables) => { onSuccess: (_data, variables) => {
const appLabel = const appLabel =
variables.appType === "claude" variables.appType === "claude"
@@ -138,8 +149,8 @@ export function useProxyStatus() {
{ closeButton: true }, { closeButton: true },
); );
queryClient.invalidateQueries({ queryKey: proxyKeys.status }); queryClient.invalidateQueries({ queryKey: ["proxyStatus"] });
queryClient.invalidateQueries({ queryKey: proxyKeys.takeoverStatus }); queryClient.invalidateQueries({ queryKey: ["proxyTakeoverStatus"] });
}, },
onError: (error: Error) => { onError: (error: Error) => {
const detail = const detail =
@@ -154,10 +165,60 @@ export function useProxyStatus() {
}, },
}); });
// 代理模式切换供应商(热切换)
const switchProxyProviderMutation = useMutation({
mutationFn: ({
appType,
providerId,
}: {
appType: string;
providerId: string;
}) => invoke("switch_proxy_provider", { appType, providerId }),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["proxyStatus"] });
},
onError: (error: Error) => {
const detail =
extractErrorMessage(error) ||
t("common.unknown", { defaultValue: "未知错误" });
toast.error(
t("proxy.switchFailed", {
error: detail,
defaultValue: `切换失败: ${detail}`,
}),
);
},
});
// 检查是否运行中
const checkRunning = async () => {
try {
return await invoke<boolean>("is_proxy_running");
} catch {
return false;
}
};
// 检查接管状态
const checkTakeoverActive = async () => {
try {
return await invoke<boolean>("is_live_takeover_active");
} catch {
return false;
}
};
return { return {
status, status,
isLoading,
isRunning: status?.running || false, isRunning: status?.running || false,
takeoverStatus, takeoverStatus,
isTakeoverActive:
takeoverStatus?.claude ||
takeoverStatus?.codex ||
takeoverStatus?.gemini ||
takeoverStatus?.grokbuild ||
false,
// 启动/停止(总开关) // 启动/停止(总开关)
startProxyServer: startProxyServerMutation.mutateAsync, startProxyServer: startProxyServerMutation.mutateAsync,
@@ -167,9 +228,17 @@ export function useProxyStatus() {
// 按应用接管开关 // 按应用接管开关
setTakeoverForApp: setTakeoverForAppMutation.mutateAsync, setTakeoverForApp: setTakeoverForAppMutation.mutateAsync,
// 代理模式下切换供应商
switchProxyProvider: switchProxyProviderMutation.mutateAsync,
// 状态检查
checkRunning,
checkTakeoverActive,
// 加载状态 // 加载状态
isStarting: startProxyServerMutation.isPending, isStarting: startProxyServerMutation.isPending,
isStoppingServer: stopProxyServerMutation.isPending, isStoppingServer: stopProxyServerMutation.isPending,
isStopping: stopWithRestoreMutation.isPending,
isPending: isPending:
startProxyServerMutation.isPending || startProxyServerMutation.isPending ||
stopProxyServerMutation.isPending || stopProxyServerMutation.isPending ||
+31 -5
View File
@@ -1,5 +1,6 @@
import { invoke } from "@tauri-apps/api/core"; import { invoke } from "@tauri-apps/api/core";
import type { import type {
ProxyConfig,
ProxyStatus, ProxyStatus,
ProxyServerInfo, ProxyServerInfo,
ProxyTakeoverStatus, ProxyTakeoverStatus,
@@ -15,11 +16,6 @@ export const proxyApi = {
return invoke("start_proxy_server"); return invoke("start_proxy_server");
}, },
// 停止代理服务器(不恢复已接管配置)
async stopProxyServer(): Promise<void> {
return invoke("stop_proxy_server");
},
// 停止代理服务器并恢复配置 // 停止代理服务器并恢复配置
async stopProxyWithRestore(): Promise<void> { async stopProxyWithRestore(): Promise<void> {
return invoke("stop_proxy_with_restore"); return invoke("stop_proxy_with_restore");
@@ -30,6 +26,24 @@ export const proxyApi = {
return invoke("get_proxy_status"); return invoke("get_proxy_status");
}, },
// 检查代理服务器是否正在运行
async isProxyRunning(): Promise<boolean> {
return invoke("is_proxy_running");
},
// 检查是否处于接管模式
async isLiveTakeoverActive(): Promise<boolean> {
return invoke("is_live_takeover_active");
},
// 代理模式下切换供应商
async switchProxyProvider(
appType: string,
providerId: string,
): Promise<void> {
return invoke("switch_proxy_provider", { appType, providerId });
},
// ========== 接管状态 API ========== // ========== 接管状态 API ==========
// 获取各应用接管状态 // 获取各应用接管状态
@@ -45,6 +59,18 @@ export const proxyApi = {
return invoke("set_proxy_takeover_for_app", { appType, enabled }); return invoke("set_proxy_takeover_for_app", { appType, enabled });
}, },
// ========== Legacy 代理配置 API (兼容) ==========
// 获取代理配置(旧版 v2 兼容接口)
async getProxyConfig(): Promise<ProxyConfig> {
return invoke("get_proxy_config");
},
// 更新代理配置(旧版 v2 兼容接口)
async updateProxyConfig(config: ProxyConfig): Promise<void> {
return invoke("update_proxy_config", { config });
},
// ========== v3+ 全局/应用级配置 API ========== // ========== v3+ 全局/应用级配置 API ==========
// 获取全局代理配置 // 获取全局代理配置
+2 -3
View File
@@ -3,7 +3,6 @@ import { failoverApi } from "@/lib/api/failover";
import { toast } from "sonner"; import { toast } from "sonner";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { extractErrorMessage } from "@/utils/errorUtils"; import { extractErrorMessage } from "@/utils/errorUtils";
import { proxyKeys } from "@/lib/query/proxy";
// ========== 熔断器 Hooks ========== // ========== 熔断器 Hooks ==========
@@ -48,7 +47,7 @@ export function useResetCircuitBreaker() {
}); });
// 刷新代理状态(更新 active_targets // 刷新代理状态(更新 active_targets
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: proxyKeys.status, queryKey: ["proxyStatus"],
}); });
}, },
}); });
@@ -284,7 +283,7 @@ export function useSetAutoFailoverEnabled() {
queryKey: ["providers", variables.appType], queryKey: ["providers", variables.appType],
}); });
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: proxyKeys.status, queryKey: ["proxyStatus"],
}); });
}, },
}); });
+1 -2
View File
@@ -9,7 +9,6 @@ import { extractErrorMessage } from "@/utils/errorUtils";
import { generateUUID } from "@/utils/uuid"; import { generateUUID } from "@/utils/uuid";
import { openclawKeys } from "@/hooks/useOpenClaw"; import { openclawKeys } from "@/hooks/useOpenClaw";
import { invalidateHermesProviderCaches } from "@/hooks/useHermes"; import { invalidateHermesProviderCaches } from "@/hooks/useHermes";
import { proxyKeys } from "@/lib/query/proxy";
import { usageKeys } from "@/lib/query/usage"; import { usageKeys } from "@/lib/query/usage";
import { import {
CODEX_OFFICIAL_PROVIDER_ID, CODEX_OFFICIAL_PROVIDER_ID,
@@ -287,7 +286,7 @@ export const useSwitchProviderMutation = (appId: AppId) => {
onSuccess: async () => { onSuccess: async () => {
await queryClient.invalidateQueries({ queryKey: ["providers", appId] }); await queryClient.invalidateQueries({ queryKey: ["providers", appId] });
if (appId === "claude-desktop") { if (appId === "claude-desktop") {
await queryClient.invalidateQueries({ queryKey: proxyKeys.status }); await queryClient.invalidateQueries({ queryKey: ["proxyStatus"] });
await queryClient.invalidateQueries({ await queryClient.invalidateQueries({
queryKey: ["claudeDesktopStatus"], queryKey: ["claudeDesktopStatus"],
}); });
+136 -34
View File
@@ -2,54 +2,90 @@ import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { proxyApi } from "@/lib/api/proxy"; import { proxyApi } from "@/lib/api/proxy";
import { toast } from "sonner"; import { toast } from "sonner";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import type { import type { GlobalProxyConfig, AppProxyConfig } from "@/types/proxy";
GlobalProxyConfig,
AppProxyConfig,
ProxyTakeoverStatus,
} from "@/types/proxy";
export const proxyKeys = {
status: ["proxyStatus"] as const,
takeoverStatus: ["proxyTakeoverStatus"] as const,
globalConfig: ["globalProxyConfig"] as const,
appConfig: (appType: string) => ["appProxyConfig", appType] as const,
};
// ========== 代理服务器状态 Hooks ========== // ========== 代理服务器状态 Hooks ==========
/** /**
* 获取代理服务器状态 * 获取代理服务器状态
*/ */
export function useProxyStatusQuery() { export function useProxyStatus() {
return useQuery({ return useQuery({
queryKey: proxyKeys.status, queryKey: ["proxyStatus"],
queryFn: () => proxyApi.getProxyStatus(), queryFn: () => proxyApi.getProxyStatus(),
// 仅在服务运行时轮询 refetchInterval: 5000, // 每 5 秒刷新一次
refetchInterval: (query) => (query.state.data?.running ? 2000 : false), });
// 保持之前的数据,避免闪烁 }
placeholderData: (previousData) => previousData,
/**
* 检查代理服务器是否运行
*/
export function useIsProxyRunning() {
return useQuery({
queryKey: ["proxyRunning"],
queryFn: () => proxyApi.isProxyRunning(),
refetchInterval: 2000,
});
}
/**
* 检查是否处于接管模式
*/
export function useIsLiveTakeoverActive() {
return useQuery({
queryKey: ["liveTakeoverActive"],
queryFn: () => proxyApi.isLiveTakeoverActive(),
refetchInterval: 2000,
}); });
} }
/** /**
* 获取各应用接管状态 * 获取各应用接管状态
*/ */
export function useProxyTakeoverStatus(poll = true) { export function useProxyTakeoverStatus() {
return useQuery({ return useQuery({
queryKey: proxyKeys.takeoverStatus, queryKey: ["proxyTakeoverStatus"],
queryFn: () => proxyApi.getProxyTakeoverStatus(), queryFn: () => proxyApi.getProxyTakeoverStatus(),
refetchInterval: poll ? 2000 : false, refetchInterval: 2000,
...(poll
? {}
: {
placeholderData: (previousData: ProxyTakeoverStatus | undefined) =>
previousData,
}),
}); });
} }
// ========== 代理服务器控制 Hooks ========== // ========== 代理服务器控制 Hooks ==========
/**
* 启动代理服务器
*/
export function useStartProxyServer() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: () => proxyApi.startProxyServer(),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["proxyStatus"] });
queryClient.invalidateQueries({ queryKey: ["proxyRunning"] });
queryClient.invalidateQueries({ queryKey: ["liveTakeoverActive"] });
queryClient.invalidateQueries({ queryKey: ["proxyTakeoverStatus"] });
},
});
}
/**
* 停止代理服务器
*/
export function useStopProxyServer() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: () => proxyApi.stopProxyWithRestore(),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["proxyStatus"] });
queryClient.invalidateQueries({ queryKey: ["proxyRunning"] });
queryClient.invalidateQueries({ queryKey: ["liveTakeoverActive"] });
queryClient.invalidateQueries({ queryKey: ["proxyTakeoverStatus"] });
},
});
}
/** /**
* 设置应用接管状态 * 设置应用接管状态
*/ */
@@ -60,11 +96,75 @@ export function useSetProxyTakeoverForApp() {
mutationFn: ({ appType, enabled }: { appType: string; enabled: boolean }) => mutationFn: ({ appType, enabled }: { appType: string; enabled: boolean }) =>
proxyApi.setProxyTakeoverForApp(appType, enabled), proxyApi.setProxyTakeoverForApp(appType, enabled),
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries({ queryKey: proxyKeys.takeoverStatus }); queryClient.invalidateQueries({ queryKey: ["proxyTakeoverStatus"] });
queryClient.invalidateQueries({ queryKey: ["liveTakeoverActive"] });
}, },
}); });
} }
/**
* 代理模式下切换供应商
*/
export function useSwitchProxyProvider() {
const queryClient = useQueryClient();
const { t } = useTranslation();
return useMutation({
mutationFn: ({
appType,
providerId,
}: {
appType: string;
providerId: string;
}) => proxyApi.switchProxyProvider(appType, providerId),
onSuccess: (_, variables) => {
queryClient.invalidateQueries({ queryKey: ["proxyStatus"] });
queryClient.invalidateQueries({
queryKey: ["providers", variables.appType],
});
},
onError: (error: Error) => {
toast.error(t("proxy.switchFailed", { error: error.message }));
},
});
}
// ========== Legacy 代理配置 Hooks (兼容) ==========
/**
* 获取代理配置(旧版)
*/
export function useProxyConfig() {
const queryClient = useQueryClient();
const { t } = useTranslation();
const { data: config, isLoading } = useQuery({
queryKey: ["proxyConfig"],
queryFn: () => proxyApi.getProxyConfig(),
});
const updateMutation = useMutation({
mutationFn: proxyApi.updateProxyConfig,
onSuccess: () => {
toast.success(t("proxy.settings.toast.saved"), { closeButton: true });
queryClient.invalidateQueries({ queryKey: ["proxyConfig"] });
queryClient.invalidateQueries({ queryKey: ["proxyStatus"] });
},
onError: (error: Error) => {
toast.error(
t("proxy.settings.toast.saveFailed", { error: error.message }),
);
},
});
return {
config,
isLoading,
updateConfig: updateMutation.mutateAsync,
isUpdating: updateMutation.isPending,
};
}
// ========== v3+ 全局/应用级配置 Hooks ========== // ========== v3+ 全局/应用级配置 Hooks ==========
/** /**
@@ -72,7 +172,7 @@ export function useSetProxyTakeoverForApp() {
*/ */
export function useGlobalProxyConfig() { export function useGlobalProxyConfig() {
return useQuery({ return useQuery({
queryKey: proxyKeys.globalConfig, queryKey: ["globalProxyConfig"],
queryFn: () => proxyApi.getGlobalProxyConfig(), queryFn: () => proxyApi.getGlobalProxyConfig(),
}); });
} }
@@ -89,8 +189,9 @@ export function useUpdateGlobalProxyConfig() {
proxyApi.updateGlobalProxyConfig(config), proxyApi.updateGlobalProxyConfig(config),
onSuccess: () => { onSuccess: () => {
toast.success(t("proxy.settings.toast.saved"), { closeButton: true }); toast.success(t("proxy.settings.toast.saved"), { closeButton: true });
queryClient.invalidateQueries({ queryKey: proxyKeys.globalConfig }); queryClient.invalidateQueries({ queryKey: ["globalProxyConfig"] });
queryClient.invalidateQueries({ queryKey: proxyKeys.status }); queryClient.invalidateQueries({ queryKey: ["proxyConfig"] });
queryClient.invalidateQueries({ queryKey: ["proxyStatus"] });
}, },
onError: (error: Error) => { onError: (error: Error) => {
toast.error( toast.error(
@@ -105,7 +206,7 @@ export function useUpdateGlobalProxyConfig() {
*/ */
export function useAppProxyConfig(appType: string) { export function useAppProxyConfig(appType: string) {
return useQuery({ return useQuery({
queryKey: proxyKeys.appConfig(appType), queryKey: ["appProxyConfig", appType],
queryFn: () => proxyApi.getProxyConfigForApp(appType), queryFn: () => proxyApi.getProxyConfigForApp(appType),
enabled: !!appType, enabled: !!appType,
}); });
@@ -124,13 +225,14 @@ export function useUpdateAppProxyConfig() {
onSuccess: (_, variables) => { onSuccess: (_, variables) => {
toast.success(t("proxy.settings.toast.saved"), { closeButton: true }); toast.success(t("proxy.settings.toast.saved"), { closeButton: true });
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: proxyKeys.appConfig(variables.appType), queryKey: ["appProxyConfig", variables.appType],
}); });
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: ["autoFailoverEnabled", variables.appType], queryKey: ["autoFailoverEnabled", variables.appType],
}); });
queryClient.invalidateQueries({ queryKey: ["proxyConfig"] });
queryClient.invalidateQueries({ queryKey: ["circuitBreakerConfig"] }); queryClient.invalidateQueries({ queryKey: ["circuitBreakerConfig"] });
queryClient.invalidateQueries({ queryKey: proxyKeys.status }); queryClient.invalidateQueries({ queryKey: ["proxyStatus"] });
}, },
onError: (error: Error) => { onError: (error: Error) => {
toast.error( toast.error(
+9 -2
View File
@@ -22,10 +22,17 @@ vi.mock("react-i18next", () => ({
vi.mock("@/hooks/useProxyStatus", () => ({ vi.mock("@/hooks/useProxyStatus", () => ({
useProxyStatus: () => ({ useProxyStatus: () => ({
status: null,
isLoading: false,
isRunning: false, isRunning: false,
takeoverStatus: null, isTakeoverActive: false,
startProxyServer: vi.fn(), startWithTakeover: vi.fn(),
stopWithRestore: vi.fn(), stopWithRestore: vi.fn(),
switchProxyProvider: vi.fn(),
checkRunning: vi.fn(),
checkTakeoverActive: vi.fn(),
isStarting: false,
isStopping: false,
isPending: false, isPending: false,
}), }),
})); }));
+1 -16
View File
@@ -3,7 +3,6 @@ import { renderHook, act, waitFor } from "@testing-library/react";
import { QueryClientProvider } from "@tanstack/react-query"; import { QueryClientProvider } from "@tanstack/react-query";
import { beforeEach, describe, expect, it, vi } from "vitest"; import { beforeEach, describe, expect, it, vi } from "vitest";
import { useProxyStatus } from "@/hooks/useProxyStatus"; import { useProxyStatus } from "@/hooks/useProxyStatus";
import { proxyKeys } from "@/lib/query/proxy";
import { createTestQueryClient } from "../utils/testQueryClient"; import { createTestQueryClient } from "../utils/testQueryClient";
const toastSuccessMock = vi.fn(); const toastSuccessMock = vi.fn();
@@ -100,19 +99,12 @@ describe("useProxyStatus", () => {
}); });
}); });
it("keeps the established proxy query key shapes", () => {
expect(proxyKeys.status).toEqual(["proxyStatus"]);
expect(proxyKeys.takeoverStatus).toEqual(["proxyTakeoverStatus"]);
expect(proxyKeys.globalConfig).toEqual(["globalProxyConfig"]);
expect(proxyKeys.appConfig("claude")).toEqual(["appProxyConfig", "claude"]);
});
it("shows interpolated address and port after proxy server starts", async () => { it("shows interpolated address and port after proxy server starts", async () => {
const { wrapper } = createWrapper(); const { wrapper } = createWrapper();
const { result } = renderHook(() => useProxyStatus(), { wrapper }); const { result } = renderHook(() => useProxyStatus(), { wrapper });
await waitFor(() => { await waitFor(() => {
expect(result.current.status).toBeDefined(); expect(result.current.isLoading).toBe(false);
}); });
await act(async () => { await act(async () => {
@@ -123,12 +115,5 @@ describe("useProxyStatus", () => {
"代理服务已启动 - 127.0.0.1:15721", "代理服务已启动 - 127.0.0.1:15721",
{ closeButton: true }, { closeButton: true },
); );
await act(async () => {
await result.current.stopProxyServer();
});
expect(invokeMock).toHaveBeenCalledWith("start_proxy_server");
expect(invokeMock).toHaveBeenCalledWith("stop_proxy_server");
}); });
}); });