/** * API Settings Dialog Component */ "use client"; import { useState, useEffect } from "react"; import { Settings, Cloud, CloudOff } from "lucide-react"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import * as z from "zod"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { getApiSettingsAsync, saveApiSettingsAsync, clearApiSettings, migrateToEncrypted, type ApiSettings, DEFAULT_API_SETTINGS, } from "@/lib/storage"; import { useAuth } from "@/contexts/auth-context"; import { getCloudSettings, saveCloudSettings, isCloudSyncEnabled } from "@/lib/user-api"; const formSchema = z.object({ // All API keys are optional - users only need the ones for their selected models openai_api_key: z.string().optional().or(z.literal("")), // Stock market data APIs alpha_vantage_api_key: z.string().optional().or(z.literal("")), // 美股基本面資料 finmind_api_key: z.string().optional().or(z.literal("")), // 台灣股市資料 // LLM Providers anthropic_api_key: z.string().optional().or(z.literal("")), google_api_key: z.string().optional().or(z.literal("")), grok_api_key: z.string().optional().or(z.literal("")), deepseek_api_key: z.string().optional().or(z.literal("")), qwen_api_key: z.string().optional().or(z.literal("")), // Custom endpoint custom_base_url: z.string().optional().or(z.literal("")), custom_api_key: z.string().optional().or(z.literal("")), }); type FormValues = z.infer; export function ApiSettingsDialog() { const [open, setOpen] = useState(false); const [saveSuccess, setSaveSuccess] = useState(false); const [loading, setLoading] = useState(false); const [syncStatus, setSyncStatus] = useState<"local" | "cloud" | "syncing">("local"); const { isAuthenticated } = useAuth(); const form = useForm({ resolver: zodResolver(formSchema), defaultValues: DEFAULT_API_SETTINGS, }); // Load and decrypt settings when dialog opens useEffect(() => { if (open) { setLoading(true); setSaveSuccess(false); const loadSettings = async () => { try { // First try to migrate legacy settings await migrateToEncrypted(); // If authenticated, try to load from cloud first if (isAuthenticated && isCloudSyncEnabled()) { setSyncStatus("syncing"); const cloudSettings = await getCloudSettings(); if (cloudSettings) { form.reset(cloudSettings); setSyncStatus("cloud"); return; } } // Fall back to local storage const localSettings = await getApiSettingsAsync(); form.reset(localSettings); setSyncStatus(isAuthenticated ? "cloud" : "local"); } catch (error) { console.error("Failed to load settings:", error); setSyncStatus("local"); } finally { setLoading(false); } }; loadSettings(); } }, [open, form, isAuthenticated]); const onSubmit = async (values: FormValues) => { setLoading(true); try { // Encrypt and save settings locally await saveApiSettingsAsync(values as ApiSettings); // If authenticated, also save to cloud if (isAuthenticated && isCloudSyncEnabled()) { setSyncStatus("syncing"); const cloudSaved = await saveCloudSettings(values as ApiSettings); setSyncStatus(cloudSaved ? "cloud" : "local"); } setSaveSuccess(true); setTimeout(() => { setSaveSuccess(false); setOpen(false); }, 1500); } catch (error) { console.error("Failed to save settings:", error); } finally { setLoading(false); } }; const handleClear = () => { clearApiSettings(); form.reset(DEFAULT_API_SETTINGS); }; return ( {/* @ts-ignore - React 19 type compatibility issue with Radix UI */} API 配置 設定您的 API 金鑰。這些資訊會以加密形式儲存在瀏覽器中。 🔒 已啟用 AES-256-GCM 加密保護
{/* 注意事項 */}
💡 僅需填寫您選擇的模型供應商的 API。例如,若使用 Claude 模型,只需填寫 Claude API。
{/* Stock Market Data APIs Section */}

股市資料 API(依分析市場選擇填寫)

{/* FinMind API Key - Taiwan Stocks */} ( FinMind API Token(台股) 用於獲取台灣股市資料(在 finmindtrade.com 註冊取得) )} /> {/* Alpha Vantage API Key - US Stocks */} ( Alpha Vantage API Key(美股) 用於獲取美股基本面數據(分析美股時建議填寫) )} />
{/* LLM Providers Section */}

LLM 模型供應商(依選擇的模型填寫)

{/* OpenAI API Key */} ( OpenAI API Key 用於 OpenAI 模型(GPT-4, GPT-5, o4 等)及 OpenAI 嵌入式模型 )} /> ( Anthropic API Key 用於 Claude 模型 )} /> {/* Google API Key */} ( Google API Key 用於 Gemini 模型 )} /> {/* Grok API Key */} ( Grok (xAI) API Key 用於 Grok 模型 )} /> {/* DeepSeek API Key */} ( DeepSeek API Key 用於 DeepSeek 模型 )} /> {/* Qwen API Key */} ( Qwen (Alibaba) API Key 用於 Qwen 模型 )} />
{/* Custom Endpoint Section */}

自訂端點(進階選項)

{/* Custom Base URL */} ( 自訂 Base URL 若設定此項,將覆蓋所有模型的預設端點 )} /> {/* Custom API Key */} ( 自訂端點 API Key 配合自訂 Base URL 使用的 API Key )} />
{saveSuccess && (
✓ 設定已成功儲存
)}
); }