Files
CC-Switch/src/components/providers/forms/hooks/useApiKeyLink.ts
T
YoVinchen 8a05e7bd3d feat(gemini): add Gemini provider integration (#202)
* feat(gemini): add Gemini provider integration

- Add gemini_config.rs module for .env file parsing
- Extend AppType enum to support Gemini
- Implement GeminiConfigEditor and GeminiFormFields components
- Add GeminiIcon with standardized 1024x1024 viewBox
- Add Gemini provider presets configuration
- Update i18n translations for Gemini support
- Extend ProviderService and McpService for Gemini

* fix(gemini): resolve TypeScript errors, add i18n support, and fix MCP logic

**Critical Fixes:**
- Fix TS2741 errors in tests/msw/state.ts by adding missing Gemini type definitions
- Fix ProviderCard.extractApiUrl to support GOOGLE_GEMINI_BASE_URL display
- Add missing apps.gemini i18n keys (zh/en) for proper app name display
- Fix MCP service Gemini cross-app duplication logic to prevent self-copy

**Technical Details:**
- tests/msw/state.ts: Add gemini default providers, current ID, and MCP config
- ProviderCard.tsx: Check both ANTHROPIC_BASE_URL and GOOGLE_GEMINI_BASE_URL
- services/mcp.rs: Skip Gemini in sync_other_side logic with unreachable!() guards
- Run pnpm format to auto-fix code style issues

**Verification:**
-  pnpm typecheck passes
-  pnpm format completed

* feat(gemini): enhance authentication and config parsing

- Add strict and lenient .env parsing modes
- Implement PackyCode partner authentication detection
- Support Google OAuth official authentication
- Auto-configure security.auth.selectedType for PackyCode
- Add comprehensive test coverage for all auth types
- Update i18n for OAuth hints and Gemini config

---------

Co-authored-by: Jason <farion1231@gmail.com>
2025-11-12 10:47:34 +08:00

85 lines
2.5 KiB
TypeScript

import { useMemo } from "react";
import type { AppId } from "@/lib/api";
import type { ProviderCategory } from "@/types";
import type { ProviderPreset } from "@/config/claudeProviderPresets";
import type { CodexProviderPreset } from "@/config/codexProviderPresets";
import type { GeminiProviderPreset } from "@/config/geminiProviderPresets";
type PresetEntry = {
id: string;
preset: ProviderPreset | CodexProviderPreset | GeminiProviderPreset;
};
interface UseApiKeyLinkProps {
appId: AppId;
category?: ProviderCategory;
selectedPresetId: string | null;
presetEntries: PresetEntry[];
formWebsiteUrl: string;
}
/**
* 管理 API Key 获取链接的显示和 URL
*/
export function useApiKeyLink({
appId,
category,
selectedPresetId,
presetEntries,
formWebsiteUrl,
}: UseApiKeyLinkProps) {
// 判断是否显示 API Key 获取链接
const shouldShowApiKeyLink = useMemo(() => {
return (
category !== "official" &&
(category === "cn_official" ||
category === "aggregator" ||
category === "third_party")
);
}, [category]);
// 获取当前预设条目
const currentPresetEntry = useMemo(() => {
if (selectedPresetId && selectedPresetId !== "custom") {
return presetEntries.find((item) => item.id === selectedPresetId);
}
return undefined;
}, [selectedPresetId, presetEntries]);
// 获取当前供应商的网址(用于 API Key 链接)
const getWebsiteUrl = useMemo(() => {
if (currentPresetEntry) {
const preset = currentPresetEntry.preset;
// 对于 cn_official、aggregator、third_party,优先使用 apiKeyUrl(可能包含推广参数)
if (
preset.category === "cn_official" ||
preset.category === "aggregator" ||
preset.category === "third_party"
) {
return preset.apiKeyUrl || preset.websiteUrl || "";
}
return preset.websiteUrl || "";
}
return formWebsiteUrl || "";
}, [currentPresetEntry, formWebsiteUrl]);
// 提取合作伙伴信息
const isPartner = useMemo(() => {
return currentPresetEntry?.preset.isPartner ?? false;
}, [currentPresetEntry]);
const partnerPromotionKey = useMemo(() => {
return currentPresetEntry?.preset.partnerPromotionKey;
}, [currentPresetEntry]);
return {
shouldShowApiKeyLink:
appId === "claude" || appId === "codex" || appId === "gemini"
? shouldShowApiKeyLink
: false,
websiteUrl: getWebsiteUrl,
isPartner,
partnerPromotionKey,
};
}