From 718c7f50992656a97434ce5041e716145ec3a5c8 Mon Sep 17 00:00:00 2001 From: blackhao <13851610112@163.com> Date: Wed, 10 Dec 2025 21:22:14 -0600 Subject: set keys --- frontend/index.html | 4 +- frontend/public/webicon.png | Bin 0 -> 77075 bytes frontend/src/components/LeftSidebar.tsx | 193 +++++++++++++++++++++++++++++++- 3 files changed, 189 insertions(+), 8 deletions(-) create mode 100644 frontend/public/webicon.png (limited to 'frontend') diff --git a/frontend/index.html b/frontend/index.html index 072a57e..a91e052 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -2,9 +2,9 @@ - + - frontend + ContextFlow
diff --git a/frontend/public/webicon.png b/frontend/public/webicon.png new file mode 100644 index 0000000..0fc5a9a Binary files /dev/null and b/frontend/public/webicon.png differ diff --git a/frontend/src/components/LeftSidebar.tsx b/frontend/src/components/LeftSidebar.tsx index 1a111bf..d929dcc 100644 --- a/frontend/src/components/LeftSidebar.tsx +++ b/frontend/src/components/LeftSidebar.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useMemo, useRef, useState, useCallback } from 'react' import { useReactFlow } from 'reactflow'; import { Folder, FileText, Archive, ChevronLeft, ChevronRight, Trash2, MessageSquare, - MoreVertical, Download, Upload, Plus, RefreshCw, Edit3, Loader2, LogOut, User + MoreVertical, Download, Upload, Plus, RefreshCw, Edit3, Loader2, LogOut, User, Settings, Key, X, Eye, EyeOff } from 'lucide-react'; import useFlowStore, { type FSItem, type BlueprintDocument, type FileMeta } from '../store/flowStore'; import { useAuthStore } from '../store/authStore'; @@ -54,6 +54,60 @@ const LeftSidebar: React.FC = ({ isOpen, onToggle }) => { const [openaiPurpose, setOpenaiPurpose] = useState('user_data'); const [fileSearch, setFileSearch] = useState(''); + // User Settings Modal State + const [showUserSettings, setShowUserSettings] = useState(false); + const [openaiApiKey, setOpenaiApiKey] = useState(''); + const [geminiApiKey, setGeminiApiKey] = useState(''); + const [showOpenaiKey, setShowOpenaiKey] = useState(false); + const [showGeminiKey, setShowGeminiKey] = useState(false); + const [savingKeys, setSavingKeys] = useState(false); + const [keysMessage, setKeysMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null); + const { getAuthHeader } = useAuthStore(); + + // Load API keys when settings modal opens + useEffect(() => { + if (showUserSettings) { + fetch('/api/auth/api-keys', { + headers: { ...getAuthHeader() }, + }) + .then(res => res.json()) + .then(data => { + setOpenaiApiKey(data.openai_api_key || ''); + setGeminiApiKey(data.gemini_api_key || ''); + }) + .catch(() => {}); + } + }, [showUserSettings, getAuthHeader]); + + const handleSaveApiKeys = async () => { + setSavingKeys(true); + setKeysMessage(null); + try { + const res = await fetch('/api/auth/api-keys', { + method: 'POST', + headers: { 'Content-Type': 'application/json', ...getAuthHeader() }, + body: JSON.stringify({ + openai_api_key: openaiApiKey.includes('*') ? undefined : openaiApiKey, + gemini_api_key: geminiApiKey.includes('*') ? undefined : geminiApiKey, + }), + }); + if (res.ok) { + setKeysMessage({ type: 'success', text: 'API keys saved successfully!' }); + // Reload masked keys + const data = await fetch('/api/auth/api-keys', { + headers: { ...getAuthHeader() }, + }).then(r => r.json()); + setOpenaiApiKey(data.openai_api_key || ''); + setGeminiApiKey(data.gemini_api_key || ''); + } else { + setKeysMessage({ type: 'error', text: 'Failed to save API keys' }); + } + } catch { + setKeysMessage({ type: 'error', text: 'Network error' }); + } + setSavingKeys(false); + }; + const handleDragStart = (e: React.DragEvent, archiveId: string) => { e.dataTransfer.setData('archiveId', archiveId); e.dataTransfer.effectAllowed = 'copy'; @@ -459,15 +513,15 @@ const LeftSidebar: React.FC = ({ isOpen, onToggle }) => {
{user && ( )}
+ {/* User Settings Modal */} + {showUserSettings && ( +
setShowUserSettings(false)}> +
e.stopPropagation()} + > + {/* Header */} +
+

User Settings

+ +
+ + {/* Content */} +
+ {/* User Info */} +
+
+ +
+
+

{user?.username}

+

{user?.email}

+
+
+ + {/* API Keys Section */} +
+

+ API Keys +

+ + {/* OpenAI API Key */} +
+ +
+ setOpenaiApiKey(e.target.value)} + placeholder="sk-..." + className={`w-full px-3 py-2 pr-10 rounded-lg text-sm ${ + isDark + ? 'bg-gray-700 border-gray-600 text-white placeholder-gray-500' + : 'bg-white border-gray-300 text-gray-900 placeholder-gray-400' + } border focus:outline-none focus:ring-2 focus:ring-blue-500`} + /> + +
+
+ + {/* Gemini API Key */} +
+ +
+ setGeminiApiKey(e.target.value)} + placeholder="AI..." + className={`w-full px-3 py-2 pr-10 rounded-lg text-sm ${ + isDark + ? 'bg-gray-700 border-gray-600 text-white placeholder-gray-500' + : 'bg-white border-gray-300 text-gray-900 placeholder-gray-400' + } border focus:outline-none focus:ring-2 focus:ring-blue-500`} + /> + +
+
+ + {/* Save Button */} + + + {/* Message */} + {keysMessage && ( +

+ {keysMessage.text} +

+ )} +
+
+ + {/* Footer */} +
+ +
+
+
+ )} + {/* Tabs */}