mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-27 16:26:16 +08:00
94da8ca89d
Add comprehensive UI for failover management: Components: - ProviderHealthBadge: Display provider health status with color coding - CircuitBreakerConfigPanel: Configure failure/success thresholds, timeout duration, and error rate limits - AutoFailoverConfigPanel: Manage proxy targets with drag-and-drop priority ordering and individual enable/disable controls - ProxyPanel: Integrate failover tabs for unified proxy management Provider enhancements: - ProviderCard: Show health badge and proxy target indicator - ProviderActions: Add "Set as Proxy Target" action - EditProviderDialog: Add is_proxy_target toggle - ProviderList: Support proxy target filtering mode Other: - Update App.tsx routing for settings integration - Update useProviderActions hook with proxy target mutation - Fix ProviderList tests for updated component API
148 lines
4.1 KiB
TypeScript
148 lines
4.1 KiB
TypeScript
import { useCallback } from "react";
|
|
import { useQueryClient } from "@tanstack/react-query";
|
|
import { toast } from "sonner";
|
|
import { useTranslation } from "react-i18next";
|
|
import { providersApi, settingsApi, type AppId } from "@/lib/api";
|
|
import type { Provider, UsageScript } from "@/types";
|
|
import {
|
|
useAddProviderMutation,
|
|
useUpdateProviderMutation,
|
|
useDeleteProviderMutation,
|
|
useSwitchProviderMutation,
|
|
} from "@/lib/query";
|
|
import { extractErrorMessage } from "@/utils/errorUtils";
|
|
|
|
/**
|
|
* Hook for managing provider actions (add, update, delete, switch)
|
|
* Extracts business logic from App.tsx
|
|
*/
|
|
export function useProviderActions(activeApp: AppId) {
|
|
const { t } = useTranslation();
|
|
const queryClient = useQueryClient();
|
|
|
|
const addProviderMutation = useAddProviderMutation(activeApp);
|
|
const updateProviderMutation = useUpdateProviderMutation(activeApp);
|
|
const deleteProviderMutation = useDeleteProviderMutation(activeApp);
|
|
const switchProviderMutation = useSwitchProviderMutation(activeApp);
|
|
|
|
// Claude 插件同步逻辑
|
|
const syncClaudePlugin = useCallback(
|
|
async (provider: Provider) => {
|
|
if (activeApp !== "claude") return;
|
|
|
|
try {
|
|
const settings = await settingsApi.get();
|
|
if (!settings?.enableClaudePluginIntegration) {
|
|
return;
|
|
}
|
|
|
|
const isOfficial = provider.category === "official";
|
|
await settingsApi.applyClaudePluginConfig({ official: isOfficial });
|
|
|
|
// 静默执行,不显示成功通知
|
|
} catch (error) {
|
|
const detail =
|
|
extractErrorMessage(error) ||
|
|
t("notifications.syncClaudePluginFailed", {
|
|
defaultValue: "同步 Claude 插件失败",
|
|
});
|
|
toast.error(detail, { duration: 4200 });
|
|
}
|
|
},
|
|
[activeApp, t],
|
|
);
|
|
|
|
// 添加供应商
|
|
const addProvider = useCallback(
|
|
async (provider: Omit<Provider, "id">) => {
|
|
await addProviderMutation.mutateAsync(provider);
|
|
},
|
|
[addProviderMutation],
|
|
);
|
|
|
|
// 更新供应商
|
|
const updateProvider = useCallback(
|
|
async (provider: Provider) => {
|
|
await updateProviderMutation.mutateAsync(provider);
|
|
|
|
// 更新托盘菜单(失败不影响主操作)
|
|
try {
|
|
await providersApi.updateTrayMenu();
|
|
} catch (trayError) {
|
|
console.error(
|
|
"Failed to update tray menu after updating provider",
|
|
trayError,
|
|
);
|
|
}
|
|
},
|
|
[updateProviderMutation],
|
|
);
|
|
|
|
// 切换供应商
|
|
const switchProvider = useCallback(
|
|
async (provider: Provider) => {
|
|
try {
|
|
await switchProviderMutation.mutateAsync(provider.id);
|
|
await syncClaudePlugin(provider);
|
|
} catch {
|
|
// 错误提示由 mutation 与同步函数处理
|
|
}
|
|
},
|
|
[switchProviderMutation, syncClaudePlugin],
|
|
);
|
|
|
|
// 删除供应商
|
|
const deleteProvider = useCallback(
|
|
async (id: string) => {
|
|
await deleteProviderMutation.mutateAsync(id);
|
|
},
|
|
[deleteProviderMutation],
|
|
);
|
|
|
|
// 保存用量脚本
|
|
const saveUsageScript = useCallback(
|
|
async (provider: Provider, script: UsageScript) => {
|
|
try {
|
|
const updatedProvider: Provider = {
|
|
...provider,
|
|
meta: {
|
|
...provider.meta,
|
|
usage_script: script,
|
|
},
|
|
};
|
|
|
|
await providersApi.update(updatedProvider, activeApp);
|
|
await queryClient.invalidateQueries({
|
|
queryKey: ["providers", activeApp],
|
|
});
|
|
toast.success(
|
|
t("provider.usageSaved", {
|
|
defaultValue: "用量查询配置已保存",
|
|
}),
|
|
);
|
|
} catch (error) {
|
|
const detail =
|
|
extractErrorMessage(error) ||
|
|
t("provider.usageSaveFailed", {
|
|
defaultValue: "用量查询配置保存失败",
|
|
});
|
|
toast.error(detail);
|
|
}
|
|
},
|
|
[activeApp, queryClient, t],
|
|
);
|
|
|
|
return {
|
|
addProvider,
|
|
updateProvider,
|
|
switchProvider,
|
|
deleteProvider,
|
|
saveUsageScript,
|
|
isLoading:
|
|
addProviderMutation.isPending ||
|
|
updateProviderMutation.isPending ||
|
|
deleteProviderMutation.isPending ||
|
|
switchProviderMutation.isPending,
|
|
};
|
|
}
|