From 165372cfd64eda52f3cc85256cc9997a2626c60d Mon Sep 17 00:00:00 2001 From: lintsinghua <1930438860@qq.com> Date: Sun, 26 Oct 2025 13:38:17 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E7=B3=BB=E7=BB=9F=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E5=8A=9F=E8=83=BD=EF=BC=8C=E5=8F=AF=E5=9C=A8Web?= =?UTF-8?q?=E7=95=8C=E9=9D=A2=E4=BF=AE=E6=94=B9=E9=85=8D=E7=BD=AE=EF=BC=8C?= =?UTF-8?q?=E6=97=A0=E9=9C=80=E6=89=8B=E5=8A=A8=E4=BF=AE=E6=94=B9=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E6=96=87=E4=BB=B6=EF=BC=8C=E6=97=A0=E9=9C=80=E9=87=8D?= =?UTF-8?q?=E6=96=B0=E6=9E=84=E5=BB=BAdocker=E9=95=9C=E5=83=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 104 +++- README_EN.md | 111 ++++- src/app/routes.tsx | 2 +- src/components/system/SystemConfig.tsx | 652 +++++++++++++++++++++++++ src/pages/AdminDashboard.tsx | 22 +- src/shared/config/env.ts | 61 ++- 6 files changed, 879 insertions(+), 73 deletions(-) create mode 100644 src/components/system/SystemConfig.tsx diff --git a/README.md b/README.md index fb23d1b..666bf49 100644 --- a/README.md +++ b/README.md @@ -37,8 +37,9 @@ - **AI 驱动的深度分析**:超越传统静态分析,理解代码意图,发现深层逻辑问题。 - **多维度、全方位评估**:从**安全性**、**性能**、**可维护性**到**代码风格**,提供 360 度无死角的质量评估。 -- **清晰、可行的修复建议**:独创 **What-Why-How** 模式,不仅告诉您“是什么”问题,还解释“为什么”,并提供“如何修复”的具体代码示例。 +- **清晰、可行的修复建议**:独创 **What-Why-How** 模式,不仅告诉您"是什么"问题,还解释"为什么",并提供"如何修复"的具体代码示例。 - **多平台LLM/本地LLM支持**: 已实现 10+ 主流平台API调用功能(Gemini、OpenAI、Claude、通义千问、DeepSeek、智谱AI、Kimi、文心一言、MiniMax、豆包、Ollama本地大模型),支持用户自由配置和切换。 +- **可视化运行时配置**:无需重新构建镜像,直接在浏览器中配置所有 LLM 参数和 API Keys,支持 API 中转站,配置保存在本地浏览器,安全便捷。 - **现代化、高颜值的用户界面**:基于 React + TypeScript 构建,提供流畅、直观的操作体验。 ## 🎬 项目演示 @@ -72,19 +73,23 @@ git clone https://github.com/lintsinghua/XCodeReviewer.git cd XCodeReviewer -# 2. 配置环境变量 -cp .env.example .env -# 编辑 .env 文件,至少配置以下内容: -# VITE_LLM_PROVIDER=gemini -# VITE_LLM_API_KEY=your_api_key_here - -# 3. 构建并启动 +# 2. 构建并启动(无需预先配置) docker-compose up -d -# 4. 访问应用 +# 3. 访问应用 # 浏览器打开 http://localhost:5174 ``` +**✨ 运行时配置** + +Docker 部署后,您可以直接在浏览器中配置所有设置,无需重新构建镜像: + +1. 访问 `http://localhost:5174/admin`(系统管理页面) +2. 在"系统配置"标签页中配置 LLM API Keys 和其他参数 +3. 点击保存并刷新页面即可使用 + +> 📖 **详细配置说明请参考**:[系统配置使用指南](#系统配置首次使用必看) + ### 💻 本地开发部署 适合需要开发或自定义修改的场景。 @@ -164,7 +169,16 @@ VITE_LLM_GAP_MS=1000 # 增加请求间隔
如何快速切换 LLM 平台? -只需修改 `.env` 中的 `VITE_LLM_PROVIDER` 和对应的 API Key: +**方式一:浏览器配置(推荐)** + +1. 访问 `http://localhost:5174/admin` 系统管理页面 +2. 在"系统配置"标签页选择不同的 LLM 提供商 +3. 填入对应的 API Key +4. 保存并刷新页面 + +**方式二:环境变量配置** + +修改 `.env` 中的配置: ```env # 切换到 OpenAI @@ -235,11 +249,29 @@ VITE_BAIDU_API_KEY=your_api_key:your_secret_key 获取地址:https://console.bce.baidu.com/qianfan/
+
+如何使用 API 中转站? + +许多用户使用 API 中转服务来访问 LLM(更稳定、更便宜)。配置方法: + +1. 访问系统管理页面(`/admin`) +2. 在"系统配置"标签页中: + - 选择 LLM 提供商(如 OpenAI) + - **API 基础 URL**: 填入中转站地址(如 `https://your-proxy.com/v1`) + - **API Key**: 填入中转站提供的密钥(而非官方密钥) +3. 保存并刷新页面 + +**注意**: +- 中转站 URL 通常以 `/v1` 结尾(OpenAI 兼容格式) +- 使用中转站的 API Key,不是官方的 +- 确认中转站支持你选择的 AI 模型 +
+
如何备份本地数据库? 本地数据存储在浏览器 IndexedDB 中: -- 在应用的"数据库管理"页面导出为 JSON 文件 +- 在应用的"系统管理"页面导出为 JSON 文件 - 通过导入 JSON 文件恢复数据 - 注意:清除浏览器数据会删除所有本地数据
@@ -356,18 +388,28 @@ VITE_OPENAI_API_KEY=your_openai_key
-💾 本地数据库管理 +⚙️ 系统管理 -- **三种数据库模式**: - - 🏠 **本地模式**:使用浏览器 IndexedDB,数据完全本地化,隐私安全 - - ☁️ **云端模式**:使用 Supabase,支持多设备同步 - - 🎭 **演示模式**:无需配置,快速体验功能 -- **数据管理功能**: +访问 `/admin` 页面,提供完整的系统配置和数据管理功能: + +- **🔧 可视化配置管理**(运行时配置): + - 🎯 **LLM 配置**:在浏览器中直接配置 API Keys、模型、超时等参数 + - 🔑 **平台密钥**:管理 10+ LLM 平台的 API Keys,支持快速切换 + - ⚡ **分析参数**:调整并发数、间隔时间、最大文件数等 + - 🌐 **API 中转站支持**:轻松配置第三方 API 代理服务 + - 💾 **配置优先级**:运行时配置 > 构建时配置,无需重新构建镜像 + +- **💾 数据库管理**: + - 🏠 **三种模式**:本地 IndexedDB / Supabase 云端 / 演示模式 - 📤 **导出备份**:将数据导出为 JSON 文件 - 📥 **导入恢复**:从备份文件恢复数据 - 🗑️ **清空数据**:一键清理所有本地数据 - 📊 **存储监控**:实时查看存储空间使用情况 -- **智能统计**:项目、任务、问题的完整统计和可视化展示 + +- **📈 数据概览**: + - 项目、任务、问题的完整统计 + - 可视化图表展示质量趋势 + - 存储使用情况分析
## 🛠️ 技术栈 @@ -397,6 +439,7 @@ XCodeReviewer/ │ ├── components/ # React 组件 │ │ ├── layout/ # 布局组件 (Header, Footer, PageMeta) │ │ ├── ui/ # UI 组件库 (基于 Radix UI) +│ │ ├── system/ # 系统配置组件 │ │ ├── database/ # 数据库管理组件 │ │ └── debug/ # 调试组件 │ ├── pages/ # 页面组件 @@ -404,7 +447,7 @@ XCodeReviewer/ │ │ ├── Projects.tsx # 项目管理 │ │ ├── InstantAnalysis.tsx # 即时分析 │ │ ├── AuditTasks.tsx # 审计任务 -│ │ └── AdminDashboard.tsx # 数据库管理 +│ │ └── AdminDashboard.tsx # 系统管理 │ ├── features/ # 功能模块 │ │ ├── analysis/ # 分析相关服务 │ │ │ └── services/ # AI 代码分析引擎 @@ -432,6 +475,26 @@ XCodeReviewer/ ## 🎯 使用指南 +### 系统配置(首次使用必看) + +访问 `/admin` 系统管理页面,在"系统配置"标签页中配置: + +#### 1. **配置 LLM 提供商** +- 选择您要使用的 LLM 平台(Gemini、OpenAI、Claude 等) +- 填入 API Key(支持通用 Key 或平台专用 Key) +- 可选:配置模型名称、API 基础 URL(用于中转站) + +#### 2. **配置 API 中转站**(如果使用) +- 在"API 基础 URL"中填入中转站地址(如 `https://your-proxy.com/v1`) +- 填入中转站提供的 API Key +- 保存配置 + +#### 3. **调整分析参数**(可选) +- 最大分析文件数、并发请求数、请求间隔 +- 输出语言(中文/英文) + +**配置完成后点击"保存所有更改"并刷新页面即可使用。** + ### 即时代码分析 1. 访问 `/instant-analysis` 页面 2. 选择编程语言(支持 10+ 种语言) @@ -579,8 +642,9 @@ pnpm lint - **✅ 多平台LLM支持**: 已实现 10+ 主流平台API调用功能(Gemini、OpenAI、Claude、通义千问、DeepSeek、智谱AI、Kimi、文心一言、MiniMax、豆包、Ollama本地大模型),支持用户自由配置和切换 - **✅ 本地模型支持**: 已加入对 Ollama 本地大模型的调用功能,满足数据隐私需求 -- **Multi-Agent Collaboration**: 考虑引入多智能体协作架构,会实现`Agent+人工对话`反馈的功能,包括多轮对话流程展示,人工对话中断干涉等,以获得更清晰、透明、监督性的审计过程,提升审计质量 +- **✅ 可视化配置管理**: 已实现运行时配置系统,支持在浏览器中直接配置所有 LLM 参数、API Keys,支持 API 中转站,无需重新构建镜像 - **✅ 专业报告文件生成**: 根据不同的需求生成相关格式的专业审计报告文件,支持文件报告格式定制等 +- **Multi-Agent Collaboration**: 考虑引入多智能体协作架构,会实现`Agent+人工对话`反馈的功能,包括多轮对话流程展示,人工对话中断干涉等,以获得更清晰、透明、监督性的审计过程,提升审计质量 - **审计标准自定义**: 不同团队有自己的编码规范,不同项目有特定的安全要求,也正是我们这个项目想后续做的东西。当前的版本还属于一个“半黑盒模式”,项目通过 Prompt 工程来引导分析方向和定义审计标准,实际分析效果由强大的预训练AI 模型内置知识决定。后续将结合强化学习、监督学习微调等方法,开发以支持自定义规则配置,通过YAML或者JSON定义团队特定规则,提供常见框架的最佳实践模板等等,以获得更加符合需求和标准的审计结果 --- diff --git a/README_EN.md b/README_EN.md index ac977ce..6311518 100644 --- a/README_EN.md +++ b/README_EN.md @@ -39,6 +39,7 @@ In the fast-paced world of software development, ensuring code quality is crucia - **🎯 Multi-dimensional, Comprehensive Assessment**: From **security**, **performance**, **maintainability** to **code style**, providing 360-degree quality evaluation. - **💡 Clear, Actionable Fix Suggestions**: Innovative **What-Why-How** approach that not only tells you "what" the problem is, but also explains "why" and provides "how to fix" with specific code examples. - **✅ Multi-Platform LLM/Local Model Support**: Implemented API calling functionality for 10+ mainstream platforms (Gemini, OpenAI, Claude, Qwen, DeepSeek, Zhipu AI, Kimi, ERNIE, MiniMax, Doubao, Ollama Local Models), with support for free configuration and switching +- **⚙️ Visual Runtime Configuration**: Configure all LLM parameters and API Keys directly in the browser without rebuilding images. Supports API relay services, with configurations saved locally in the browser for security and convenience. - **✨ Modern, Beautiful User Interface**: Built with React + TypeScript, providing a smooth and intuitive user experience. ## 🎬 Project Demo @@ -72,19 +73,29 @@ One-click deployment with Docker, no Node.js environment required: git clone https://github.com/lintsinghua/XCodeReviewer.git cd XCodeReviewer -# 2. Configure environment variables -cp .env.example .env -# Edit .env file, configure at least: -# VITE_LLM_PROVIDER=gemini -# VITE_LLM_API_KEY=your_api_key_here - -# 3. Build and start +# 2. Build and start (no pre-configuration needed) docker-compose up -d -# 4. Access the application +# 3. Access the application # Open http://localhost:5174 in your browser ``` +**✨ New Feature: Runtime Configuration** + +After Docker deployment, you can configure all settings directly in the browser without rebuilding the image: + +1. Visit `http://localhost:5174/admin` (System Management page) +2. Configure LLM API Keys and other parameters in the "System Configuration" tab +3. Click save and refresh the page to use + +Benefits: +- ✅ No need to hardcode API Keys in Docker images (more secure) +- ✅ Modify configuration anytime without rebuilding +- ✅ Support for API relay services +- ✅ Quickly switch between different LLM platforms + +> 📖 **For detailed configuration instructions, see**: [System Configuration Guide](#system-configuration-first-time-setup) + ### 💻 Local Development Deployment Suitable for development or custom modifications. @@ -164,7 +175,16 @@ VITE_LLM_GAP_MS=1000 # Increase request interval
How to quickly switch LLM platforms? -Simply modify `VITE_LLM_PROVIDER` and the corresponding API Key in `.env`: +**Method 1: Browser Configuration (Recommended)** + +1. Visit `http://localhost:5174/admin` System Management page +2. Select different LLM provider in the "System Configuration" tab +3. Enter the corresponding API Key +4. Save and refresh the page + +**Method 2: Environment Variable Configuration** + +Modify configuration in `.env`: ```env # Switch to OpenAI @@ -235,11 +255,29 @@ VITE_BAIDU_API_KEY=your_api_key:your_secret_key Get from: https://console.bce.baidu.com/qianfan/
+
+How to use API relay services? + +Many users use API relay services to access LLMs (more stable and cheaper). Configuration method: + +1. Visit System Management page (`/admin`) +2. In the "System Configuration" tab: + - Select LLM provider (e.g., OpenAI) + - **API Base URL**: Enter relay service address (e.g., `https://your-proxy.com/v1`) + - **API Key**: Enter the key provided by the relay service (not the official key) +3. Save and refresh the page + +**Notes**: +- Relay service URLs usually end with `/v1` (OpenAI-compatible format) +- Use the relay service's API Key, not the official one +- Confirm that the relay service supports your chosen AI model +
+
How to backup local database? Local data is stored in browser IndexedDB: -- Export as JSON file from "Database Management" page +- Export as JSON file from "System Management" page - Import JSON file to restore data - Note: Clearing browser data will delete all local data
@@ -357,18 +395,28 @@ For cloud data sync:
-💾 Local Database Management +⚙️ System Management -- **Three Database Modes**: - - 🏠 **Local Mode**: Uses browser IndexedDB, data is completely localized, privacy-secure - - ☁️ **Cloud Mode**: Uses Supabase, supports multi-device synchronization - - 🎭 **Demo Mode**: No configuration needed, quick feature preview -- **Data Management Features**: +Visit `/admin` page for complete system configuration and data management features: + +- **🔧 Visual Configuration Management** (Runtime Configuration): + - 🎯 **LLM Configuration**: Configure API Keys, models, timeout, and other parameters directly in the browser + - 🔑 **Platform Keys**: Manage API Keys for 10+ LLM platforms with quick switching support + - ⚡ **Analysis Parameters**: Adjust concurrency, interval time, max files, etc. + - 🌐 **API Relay Support**: Easily configure third-party API relay services + - 💾 **Configuration Priority**: Runtime config > Build-time config, no need to rebuild images + +- **💾 Database Management**: + - 🏠 **Three Modes**: Local IndexedDB / Supabase Cloud / Demo Mode - 📤 **Export Backup**: Export data as JSON files - 📥 **Import Recovery**: Restore data from backup files - 🗑️ **Clear Data**: One-click cleanup of all local data - 📊 **Storage Monitoring**: Real-time view of storage space usage -- **Smart Statistics**: Complete statistics and visualization of projects, tasks, and issues + +- **📈 Data Overview**: + - Complete statistics for projects, tasks, and issues + - Visual charts showing quality trends + - Storage usage analysis
## 🛠️ Tech Stack @@ -399,6 +447,7 @@ XCodeReviewer/ │ ├── components/ # React components │ │ ├── layout/ # Layout components (Header, Footer, PageMeta) │ │ ├── ui/ # UI component library (based on Radix UI) +│ │ ├── system/ # System configuration components │ │ ├── database/ # Database management components │ │ └── debug/ # Debug components │ ├── pages/ # Page components @@ -406,7 +455,7 @@ XCodeReviewer/ │ │ ├── Projects.tsx # Project management │ │ ├── InstantAnalysis.tsx # Instant analysis │ │ ├── AuditTasks.tsx # Audit tasks -│ │ └── AdminDashboard.tsx # Database management +│ │ └── AdminDashboard.tsx # System management │ ├── features/ # Feature modules │ │ ├── analysis/ # Analysis related services │ │ │ └── services/ # AI code analysis engine @@ -434,6 +483,26 @@ XCodeReviewer/ ## 🎯 Usage Guide +### System Configuration (First-Time Setup) + +Visit `/admin` System Management page and configure in the "System Configuration" tab: + +#### 1. **Configure LLM Provider** +- Select the LLM platform you want to use (Gemini, OpenAI, Claude, etc.) +- Enter API Key (supports universal Key or platform-specific Key) +- Optional: Configure model name, API base URL (for relay services) + +#### 2. **Configure API Relay Service** (if using) +- Enter relay service address in "API Base URL" (e.g., `https://your-proxy.com/v1`) +- Enter the API Key provided by the relay service +- Save configuration + +#### 3. **Adjust Analysis Parameters** (optional) +- Max analyze files, concurrent requests, request interval +- Output language (Chinese/English) + +**After configuration, click "Save All Changes" and refresh the page to use.** + ### Instant Code Analysis 1. Visit the `/instant-analysis` page 2. Select programming language (supports 10+ languages) @@ -580,10 +649,10 @@ Currently, XCodeReviewer is in rapid prototype validation stage. Based on projec - ✅ **Multi-Platform LLM Support**: Implemented API integration for 10+ mainstream platforms (Gemini, OpenAI, Claude, Qwen, DeepSeek, Zhipu AI, Kimi, ERNIE, MiniMax, Doubao, Ollama), with flexible configuration and switching - ✅ **Local Model Support**: Added Ollama local model integration to meet data privacy requirements -- ✅ **Local Database Support**: Implemented IndexedDB-based local database for fully localized data storage and privacy protection -- **Multi-Agent Collaboration**: Introduce multi-agent architecture with `Agent + Human Dialogue` feedback, including multi-round dialogue visualization and human intervention for clearer, transparent, and supervised audit processes +- ✅ **Visual Configuration Management**: Implemented runtime configuration system supporting browser-based configuration of all LLM parameters and API Keys, API relay service support, no need to rebuild images - ✅ **Professional Report Generation**: Generate professional audit reports in various formats based on different needs, with customizable templates and format configurations -- **Custom Audit Standards**: Support custom audit rule configuration via YAML/JSON, provide best practice templates for common frameworks, and leverage reinforcement learning and supervised fi +- **Multi-Agent Collaboration**: Introduce multi-agent architecture with `Agent + Human Dialogue` feedback, including multi-round dialogue visualization and human intervention for clearer, transparent, and supervised audit processes +- **Custom Audit Standards**: Support custom audit rule configuration via YAML/JSON, provide best practice templates for common frameworks, and leverage reinforcement learning and supervised fine-tuning for more targeted and standards-compliant audit results --- diff --git a/src/app/routes.tsx b/src/app/routes.tsx index cc023d0..4a272b6 100644 --- a/src/app/routes.tsx +++ b/src/app/routes.tsx @@ -52,7 +52,7 @@ const routes: RouteConfig[] = [ visible: false, }, { - name: "数据库管理", + name: "系统管理", path: "/admin", element: , visible: true, diff --git a/src/components/system/SystemConfig.tsx b/src/components/system/SystemConfig.tsx new file mode 100644 index 0000000..1347c7a --- /dev/null +++ b/src/components/system/SystemConfig.tsx @@ -0,0 +1,652 @@ +import { useState, useEffect } from "react"; +import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Alert, AlertDescription } from "@/components/ui/alert"; +import { Badge } from "@/components/ui/badge"; +import { + Settings, + Save, + RotateCcw, + Eye, + EyeOff, + CheckCircle2, + AlertCircle, + Info, + Key, + Zap, + Globe, + Database +} from "lucide-react"; +import { toast } from "sonner"; + +// LLM 提供商配置 +const LLM_PROVIDERS = [ + { value: 'gemini', label: 'Google Gemini', icon: '🔵', category: 'international' }, + { value: 'openai', label: 'OpenAI GPT', icon: '🟢', category: 'international' }, + { value: 'claude', label: 'Anthropic Claude', icon: '🟣', category: 'international' }, + { value: 'deepseek', label: 'DeepSeek', icon: '🔷', category: 'international' }, + { value: 'qwen', label: '阿里云通义千问', icon: '🟠', category: 'domestic' }, + { value: 'zhipu', label: '智谱AI (GLM)', icon: '🔴', category: 'domestic' }, + { value: 'moonshot', label: 'Moonshot (Kimi)', icon: '🌙', category: 'domestic' }, + { value: 'baidu', label: '百度文心一言', icon: '🔵', category: 'domestic' }, + { value: 'minimax', label: 'MiniMax', icon: '⚡', category: 'domestic' }, + { value: 'doubao', label: '字节豆包', icon: '🎯', category: 'domestic' }, + { value: 'ollama', label: 'Ollama 本地模型', icon: '🖥️', category: 'local' }, +]; + +// 默认模型配置 +const DEFAULT_MODELS = { + gemini: 'gemini-2.5-flash', + openai: 'gpt-4o-mini', + claude: 'claude-3-5-sonnet-20241022', + qwen: 'qwen-turbo', + deepseek: 'deepseek-chat', + zhipu: 'glm-4-flash', + moonshot: 'moonshot-v1-8k', + baidu: 'ERNIE-3.5-8K', + minimax: 'abab6.5-chat', + doubao: 'doubao-pro-32k', + ollama: 'llama3', +}; + +interface SystemConfigData { + // LLM 配置 + llmProvider: string; + llmApiKey: string; + llmModel: string; + llmBaseUrl: string; + llmTimeout: number; + llmTemperature: number; + llmMaxTokens: number; + + // 平台专用配置 + geminiApiKey: string; + openaiApiKey: string; + claudeApiKey: string; + qwenApiKey: string; + deepseekApiKey: string; + zhipuApiKey: string; + moonshotApiKey: string; + baiduApiKey: string; + minimaxApiKey: string; + doubaoApiKey: string; + ollamaBaseUrl: string; + + // GitHub 配置 + githubToken: string; + + // 分析配置 + maxAnalyzeFiles: number; + llmConcurrency: number; + llmGapMs: number; + outputLanguage: string; +} + +const STORAGE_KEY = 'xcodereviewer_runtime_config'; + +export function SystemConfig() { + const [config, setConfig] = useState({ + llmProvider: 'gemini', + llmApiKey: '', + llmModel: '', + llmBaseUrl: '', + llmTimeout: 150000, + llmTemperature: 0.2, + llmMaxTokens: 4096, + geminiApiKey: '', + openaiApiKey: '', + claudeApiKey: '', + qwenApiKey: '', + deepseekApiKey: '', + zhipuApiKey: '', + moonshotApiKey: '', + baiduApiKey: '', + minimaxApiKey: '', + doubaoApiKey: '', + ollamaBaseUrl: 'http://localhost:11434/v1', + githubToken: '', + maxAnalyzeFiles: 40, + llmConcurrency: 2, + llmGapMs: 500, + outputLanguage: 'zh-CN', + }); + + const [showApiKeys, setShowApiKeys] = useState>({}); + const [hasChanges, setHasChanges] = useState(false); + const [configSource, setConfigSource] = useState<'runtime' | 'build'>('build'); + + // 加载配置 + useEffect(() => { + loadConfig(); + }, []); + + const loadConfig = () => { + try { + // 尝试从 localStorage 加载运行时配置 + const savedConfig = localStorage.getItem(STORAGE_KEY); + + if (savedConfig) { + const parsedConfig = JSON.parse(savedConfig); + setConfig(parsedConfig); + setConfigSource('runtime'); + toast.success("已加载运行时配置"); + } else { + // 使用构建时配置 + loadFromEnv(); + setConfigSource('build'); + } + } catch (error) { + console.error('Failed to load config:', error); + loadFromEnv(); + } + }; + + const loadFromEnv = () => { + // 从环境变量加载(构建时配置) + const envConfig: SystemConfigData = { + llmProvider: import.meta.env.VITE_LLM_PROVIDER || 'gemini', + llmApiKey: import.meta.env.VITE_LLM_API_KEY || '', + llmModel: import.meta.env.VITE_LLM_MODEL || '', + llmBaseUrl: import.meta.env.VITE_LLM_BASE_URL || '', + llmTimeout: Number(import.meta.env.VITE_LLM_TIMEOUT) || 150000, + llmTemperature: Number(import.meta.env.VITE_LLM_TEMPERATURE) || 0.2, + llmMaxTokens: Number(import.meta.env.VITE_LLM_MAX_TOKENS) || 4096, + geminiApiKey: import.meta.env.VITE_GEMINI_API_KEY || '', + openaiApiKey: import.meta.env.VITE_OPENAI_API_KEY || '', + claudeApiKey: import.meta.env.VITE_CLAUDE_API_KEY || '', + qwenApiKey: import.meta.env.VITE_QWEN_API_KEY || '', + deepseekApiKey: import.meta.env.VITE_DEEPSEEK_API_KEY || '', + zhipuApiKey: import.meta.env.VITE_ZHIPU_API_KEY || '', + moonshotApiKey: import.meta.env.VITE_MOONSHOT_API_KEY || '', + baiduApiKey: import.meta.env.VITE_BAIDU_API_KEY || '', + minimaxApiKey: import.meta.env.VITE_MINIMAX_API_KEY || '', + doubaoApiKey: import.meta.env.VITE_DOUBAO_API_KEY || '', + ollamaBaseUrl: import.meta.env.VITE_OLLAMA_BASE_URL || 'http://localhost:11434/v1', + githubToken: import.meta.env.VITE_GITHUB_TOKEN || '', + maxAnalyzeFiles: Number(import.meta.env.VITE_MAX_ANALYZE_FILES) || 40, + llmConcurrency: Number(import.meta.env.VITE_LLM_CONCURRENCY) || 2, + llmGapMs: Number(import.meta.env.VITE_LLM_GAP_MS) || 500, + outputLanguage: import.meta.env.VITE_OUTPUT_LANGUAGE || 'zh-CN', + }; + setConfig(envConfig); + }; + + const saveConfig = () => { + try { + localStorage.setItem(STORAGE_KEY, JSON.stringify(config)); + setHasChanges(false); + setConfigSource('runtime'); + toast.success("配置已保存!刷新页面后生效"); + + // 提示用户刷新页面 + setTimeout(() => { + if (window.confirm("配置已保存。是否立即刷新页面使配置生效?")) { + window.location.reload(); + } + }, 1000); + } catch (error) { + console.error('Failed to save config:', error); + toast.error("保存配置失败"); + } + }; + + const resetConfig = () => { + if (window.confirm("确定要重置为构建时配置吗?这将清除所有运行时配置。")) { + localStorage.removeItem(STORAGE_KEY); + loadFromEnv(); + setHasChanges(false); + setConfigSource('build'); + toast.success("已重置为构建时配置"); + } + }; + + const updateConfig = (key: keyof SystemConfigData, value: any) => { + setConfig(prev => ({ ...prev, [key]: value })); + setHasChanges(true); + }; + + const toggleShowApiKey = (field: string) => { + setShowApiKeys(prev => ({ ...prev, [field]: !prev[field] })); + }; + + const getCurrentApiKey = () => { + const provider = config.llmProvider.toLowerCase(); + const keyMap: Record = { + gemini: config.geminiApiKey, + openai: config.openaiApiKey, + claude: config.claudeApiKey, + qwen: config.qwenApiKey, + deepseek: config.deepseekApiKey, + zhipu: config.zhipuApiKey, + moonshot: config.moonshotApiKey, + baidu: config.baiduApiKey, + minimax: config.minimaxApiKey, + doubao: config.doubaoApiKey, + ollama: 'ollama', + }; + + return config.llmApiKey || keyMap[provider] || ''; + }; + + const isConfigured = getCurrentApiKey() !== ''; + + return ( +
+ {/* 配置状态提示 */} + + + +
+ 当前配置来源: + {configSource === 'runtime' ? ( + 运行时配置 + ) : ( + 构建时配置 + )} + + {isConfigured ? ( + + LLM 已配置 + + ) : ( + + 未配置 LLM + + )} + +
+
+ {hasChanges && ( + + )} + {configSource === 'runtime' && ( + + )} +
+
+
+ + + + + + LLM 配置 + + + + 平台密钥 + + + + 分析参数 + + + + 其他配置 + + + + {/* LLM 基础配置 */} + + + + LLM 提供商配置 + 选择和配置大语言模型服务 + + +
+ + +
+ +
+ +
+ updateConfig('llmApiKey', e.target.value)} + placeholder="留空则使用平台专用 API Key" + /> + +
+

+ 如果设置,将优先使用此 API Key;否则使用下方对应平台的专用 API Key +

+
+ +
+ + updateConfig('llmModel', e.target.value)} + placeholder={`默认:${DEFAULT_MODELS[config.llmProvider as keyof typeof DEFAULT_MODELS] || '自动'}`} + /> +

+ 留空使用默认模型 +

+
+ +
+ + updateConfig('llmBaseUrl', e.target.value)} + placeholder="例如:https://api.example.com/v1" + /> +
+

💡 使用 API 中转站?在这里填入中转站地址

+
+ 查看常见 API 中转示例 +
+

OpenAI 兼容格式:

+

• https://your-proxy.com/v1

+

• https://api.openai-proxy.org/v1

+

其他中转格式:

+

• https://your-api-gateway.com/openai

+

• https://custom-endpoint.com/api

+

⚠️ 确保中转站支持你选择的 LLM 平台

+
+
+
+
+ +
+
+ + updateConfig('llmTimeout', Number(e.target.value))} + /> +
+
+ + updateConfig('llmTemperature', Number(e.target.value))} + /> +
+
+ + updateConfig('llmMaxTokens', Number(e.target.value))} + /> +
+
+
+
+ +
+ + {/* 平台专用密钥 */} + + + + +
+

配置各平台的 API Key,方便快速切换。如果设置了通用 API Key,将优先使用通用配置。

+

+ 💡 使用 API 中转站的用户注意:这里填入的应该是中转站提供的 API Key,而不是官方 Key。 + 中转站地址请在「LLM 配置」标签页的「API 基础 URL」中填写。 +

+
+
+
+ + {[ + { key: 'geminiApiKey', label: 'Google Gemini API Key', icon: '🔵', hint: '官方:https://makersuite.google.com/app/apikey | 或使用中转站 Key' }, + { key: 'openaiApiKey', label: 'OpenAI API Key', icon: '🟢', hint: '官方:https://platform.openai.com/api-keys | 或使用中转站 Key' }, + { key: 'claudeApiKey', label: 'Claude API Key', icon: '🟣', hint: '官方:https://console.anthropic.com/ | 或使用中转站 Key' }, + { key: 'qwenApiKey', label: '通义千问 API Key', icon: '🟠', hint: '官方:https://dashscope.console.aliyun.com/ | 或使用中转站 Key' }, + { key: 'deepseekApiKey', label: 'DeepSeek API Key', icon: '🔷', hint: '官方:https://platform.deepseek.com/ | 或使用中转站 Key' }, + { key: 'zhipuApiKey', label: '智谱AI API Key', icon: '🔴', hint: '官方:https://open.bigmodel.cn/ | 或使用中转站 Key' }, + { key: 'moonshotApiKey', label: 'Moonshot API Key', icon: '🌙', hint: '官方:https://platform.moonshot.cn/ | 或使用中转站 Key' }, + { key: 'baiduApiKey', label: '百度文心 API Key', icon: '🔵', hint: '官方格式:API_KEY:SECRET_KEY | 或使用中转站 Key' }, + { key: 'minimaxApiKey', label: 'MiniMax API Key', icon: '⚡', hint: '官方:https://www.minimaxi.com/ | 或使用中转站 Key' }, + { key: 'doubaoApiKey', label: '字节豆包 API Key', icon: '🎯', hint: '官方:https://console.volcengine.com/ark | 或使用中转站 Key' }, + ].map(({ key, label, icon, hint }) => ( + + + + {icon} + {label} + + {hint} + + +
+ updateConfig(key as keyof SystemConfigData, e.target.value)} + placeholder={`输入 ${label}`} + /> + +
+
+
+ ))} + + + + + 🖥️ + Ollama 基础 URL + + 本地 Ollama 服务的 API 端点 + + + updateConfig('ollamaBaseUrl', e.target.value)} + placeholder="http://localhost:11434/v1" + /> + + +
+ + {/* 分析参数配置 */} + + + + 代码分析参数 + 调整代码分析的行为和性能 + + +
+ + updateConfig('maxAnalyzeFiles', Number(e.target.value))} + /> +

+ 单次分析任务最多处理的文件数量 +

+
+ +
+ + updateConfig('llmConcurrency', Number(e.target.value))} + /> +

+ 同时发送给 LLM 的请求数量(降低可避免速率限制) +

+
+ +
+ + updateConfig('llmGapMs', Number(e.target.value))} + /> +

+ 每个 LLM 请求之间的延迟时间 +

+
+ +
+ + +
+
+
+
+ + {/* 其他配置 */} + + + + GitHub 集成 + 配置 GitHub Personal Access Token 以访问私有仓库 + + +
+ +
+ updateConfig('githubToken', e.target.value)} + placeholder="ghp_xxxxxxxxxxxx" + /> + +
+

+ 获取:https://github.com/settings/tokens +

+
+
+
+ + + + 配置说明 + + +
+ +
+

运行时配置

+

+ 配置保存在浏览器 localStorage 中,刷新页面后立即生效。 + 可以在不重新构建 Docker 镜像的情况下修改配置。 +

+
+
+
+ +
+

配置优先级

+

+ 运行时配置 > 构建时配置。如果设置了运行时配置,将覆盖构建时的环境变量。 +

+
+
+
+ +
+

安全提示

+

+ API Keys 存储在浏览器本地,其他网站无法访问。但清除浏览器数据会删除所有配置。 +

+
+
+
+
+
+
+ + {/* 底部操作按钮 */} + {hasChanges && ( +
+ + +
+ )} +
+ ); +} + diff --git a/src/pages/AdminDashboard.tsx b/src/pages/AdminDashboard.tsx index 82aad9b..87cbe3e 100644 --- a/src/pages/AdminDashboard.tsx +++ b/src/pages/AdminDashboard.tsx @@ -16,11 +16,13 @@ import { Clock, AlertTriangle, TrendingUp, - Package + Package, + Settings } from "lucide-react"; import { api, dbMode, isLocalMode } from "@/shared/config/database"; import { DatabaseManager } from "@/components/database/DatabaseManager"; import { DatabaseStatusDetail } from "@/components/database/DatabaseStatus"; +import { SystemConfig } from "@/components/system/SystemConfig"; import { toast } from "sonner"; export default function AdminDashboard() { @@ -112,11 +114,11 @@ export default function AdminDashboard() {

- - 数据库管理 + + 系统管理

- 管理和监控本地数据库,查看存储使用情况和数据统计 + 管理系统配置、LLM设置、数据库和存储使用情况

{/* 主要内容标签页 */} - - + + + 系统配置 数据概览 存储管理 数据操作 - 设置 + 高级设置 + {/* 系统配置 */} + + + + {/* 数据概览 */}
diff --git a/src/shared/config/env.ts b/src/shared/config/env.ts index 4c2ccba..8d9488b 100644 --- a/src/shared/config/env.ts +++ b/src/shared/config/env.ts @@ -1,85 +1,98 @@ -// 环境变量配置 +// 从 localStorage 读取运行时配置 +const STORAGE_KEY = 'xcodereviewer_runtime_config'; +const getRuntimeConfig = () => { + try { + const saved = localStorage.getItem(STORAGE_KEY); + return saved ? JSON.parse(saved) : null; + } catch { + return null; + } +}; + +const runtimeConfig = getRuntimeConfig(); + +// 环境变量配置(支持运行时配置覆盖) export const env = { // ==================== LLM 通用配置 ==================== // 当前使用的LLM提供商 (gemini|openai|claude|qwen|deepseek|zhipu|moonshot|baidu|minimax|doubao|ollama) - LLM_PROVIDER: import.meta.env.VITE_LLM_PROVIDER || 'gemini', + LLM_PROVIDER: runtimeConfig?.llmProvider || import.meta.env.VITE_LLM_PROVIDER || 'gemini', // LLM API Key - LLM_API_KEY: import.meta.env.VITE_LLM_API_KEY || '', + LLM_API_KEY: runtimeConfig?.llmApiKey || import.meta.env.VITE_LLM_API_KEY || '', // LLM 模型名称 - LLM_MODEL: import.meta.env.VITE_LLM_MODEL || '', + LLM_MODEL: runtimeConfig?.llmModel || import.meta.env.VITE_LLM_MODEL || '', // LLM API 基础URL (可选,用于自定义端点或代理) - LLM_BASE_URL: import.meta.env.VITE_LLM_BASE_URL || '', + LLM_BASE_URL: runtimeConfig?.llmBaseUrl || import.meta.env.VITE_LLM_BASE_URL || '', // LLM 请求超时时间(ms) - LLM_TIMEOUT: Number(import.meta.env.VITE_LLM_TIMEOUT) || 150000, + LLM_TIMEOUT: runtimeConfig?.llmTimeout || Number(import.meta.env.VITE_LLM_TIMEOUT) || 150000, // LLM 温度参数 (0.0-2.0) - LLM_TEMPERATURE: Number(import.meta.env.VITE_LLM_TEMPERATURE) || 0.2, + LLM_TEMPERATURE: runtimeConfig?.llmTemperature !== undefined ? runtimeConfig.llmTemperature : (Number(import.meta.env.VITE_LLM_TEMPERATURE) || 0.2), // LLM 最大token数 - LLM_MAX_TOKENS: Number(import.meta.env.VITE_LLM_MAX_TOKENS) || 4096, + LLM_MAX_TOKENS: runtimeConfig?.llmMaxTokens || Number(import.meta.env.VITE_LLM_MAX_TOKENS) || 4096, // ==================== Gemini AI 配置 (兼容旧配置) ==================== - GEMINI_API_KEY: import.meta.env.VITE_GEMINI_API_KEY || '', + GEMINI_API_KEY: runtimeConfig?.geminiApiKey || import.meta.env.VITE_GEMINI_API_KEY || '', GEMINI_MODEL: import.meta.env.VITE_GEMINI_MODEL || 'gemini-2.5-flash', GEMINI_TIMEOUT_MS: Number(import.meta.env.VITE_GEMINI_TIMEOUT_MS) || 25000, // ==================== OpenAI 配置 ==================== - OPENAI_API_KEY: import.meta.env.VITE_OPENAI_API_KEY || '', + OPENAI_API_KEY: runtimeConfig?.openaiApiKey || import.meta.env.VITE_OPENAI_API_KEY || '', OPENAI_MODEL: import.meta.env.VITE_OPENAI_MODEL || 'gpt-4o-mini', OPENAI_BASE_URL: import.meta.env.VITE_OPENAI_BASE_URL || '', // ==================== Claude 配置 ==================== - CLAUDE_API_KEY: import.meta.env.VITE_CLAUDE_API_KEY || '', + CLAUDE_API_KEY: runtimeConfig?.claudeApiKey || import.meta.env.VITE_CLAUDE_API_KEY || '', CLAUDE_MODEL: import.meta.env.VITE_CLAUDE_MODEL || 'claude-3-5-sonnet-20241022', // ==================== 通义千问 配置 ==================== - QWEN_API_KEY: import.meta.env.VITE_QWEN_API_KEY || '', + QWEN_API_KEY: runtimeConfig?.qwenApiKey || import.meta.env.VITE_QWEN_API_KEY || '', QWEN_MODEL: import.meta.env.VITE_QWEN_MODEL || 'qwen-turbo', // ==================== DeepSeek 配置 ==================== - DEEPSEEK_API_KEY: import.meta.env.VITE_DEEPSEEK_API_KEY || '', + DEEPSEEK_API_KEY: runtimeConfig?.deepseekApiKey || import.meta.env.VITE_DEEPSEEK_API_KEY || '', DEEPSEEK_MODEL: import.meta.env.VITE_DEEPSEEK_MODEL || 'deepseek-chat', // ==================== 智谱AI 配置 ==================== - ZHIPU_API_KEY: import.meta.env.VITE_ZHIPU_API_KEY || '', + ZHIPU_API_KEY: runtimeConfig?.zhipuApiKey || import.meta.env.VITE_ZHIPU_API_KEY || '', ZHIPU_MODEL: import.meta.env.VITE_ZHIPU_MODEL || 'glm-4-flash', // ==================== Moonshot 配置 ==================== - MOONSHOT_API_KEY: import.meta.env.VITE_MOONSHOT_API_KEY || '', + MOONSHOT_API_KEY: runtimeConfig?.moonshotApiKey || import.meta.env.VITE_MOONSHOT_API_KEY || '', MOONSHOT_MODEL: import.meta.env.VITE_MOONSHOT_MODEL || 'moonshot-v1-8k', // ==================== 百度文心一言 配置 ==================== - BAIDU_API_KEY: import.meta.env.VITE_BAIDU_API_KEY || '', + BAIDU_API_KEY: runtimeConfig?.baiduApiKey || import.meta.env.VITE_BAIDU_API_KEY || '', BAIDU_MODEL: import.meta.env.VITE_BAIDU_MODEL || 'ERNIE-3.5-8K', // ==================== MiniMax 配置 ==================== - MINIMAX_API_KEY: import.meta.env.VITE_MINIMAX_API_KEY || '', + MINIMAX_API_KEY: runtimeConfig?.minimaxApiKey || import.meta.env.VITE_MINIMAX_API_KEY || '', MINIMAX_MODEL: import.meta.env.VITE_MINIMAX_MODEL || 'abab6.5-chat', // ==================== 豆包 配置 ==================== - DOUBAO_API_KEY: import.meta.env.VITE_DOUBAO_API_KEY || '', + DOUBAO_API_KEY: runtimeConfig?.doubaoApiKey || import.meta.env.VITE_DOUBAO_API_KEY || '', DOUBAO_MODEL: import.meta.env.VITE_DOUBAO_MODEL || 'doubao-pro-32k', // ==================== Ollama 本地模型配置 ==================== OLLAMA_API_KEY: import.meta.env.VITE_OLLAMA_API_KEY || 'ollama', OLLAMA_MODEL: import.meta.env.VITE_OLLAMA_MODEL || 'llama3', - OLLAMA_BASE_URL: import.meta.env.VITE_OLLAMA_BASE_URL || 'http://localhost:11434/v1', + OLLAMA_BASE_URL: runtimeConfig?.ollamaBaseUrl || import.meta.env.VITE_OLLAMA_BASE_URL || 'http://localhost:11434/v1', // ==================== Supabase 配置 ==================== SUPABASE_URL: import.meta.env.VITE_SUPABASE_URL || '', SUPABASE_ANON_KEY: import.meta.env.VITE_SUPABASE_ANON_KEY || '', // ==================== GitHub 配置 ==================== - GITHUB_TOKEN: import.meta.env.VITE_GITHUB_TOKEN || '', + GITHUB_TOKEN: runtimeConfig?.githubToken || import.meta.env.VITE_GITHUB_TOKEN || '', // ==================== 应用配置 ==================== APP_ID: import.meta.env.VITE_APP_ID || 'xcodereviewer', // ==================== 分析配置 ==================== - MAX_ANALYZE_FILES: Number(import.meta.env.VITE_MAX_ANALYZE_FILES) || 40, - LLM_CONCURRENCY: Number(import.meta.env.VITE_LLM_CONCURRENCY) || 2, - LLM_GAP_MS: Number(import.meta.env.VITE_LLM_GAP_MS) || 500, + MAX_ANALYZE_FILES: runtimeConfig?.maxAnalyzeFiles || Number(import.meta.env.VITE_MAX_ANALYZE_FILES) || 40, + LLM_CONCURRENCY: runtimeConfig?.llmConcurrency || Number(import.meta.env.VITE_LLM_CONCURRENCY) || 2, + LLM_GAP_MS: runtimeConfig?.llmGapMs || Number(import.meta.env.VITE_LLM_GAP_MS) || 500, // ==================== 语言配置 ==================== - OUTPUT_LANGUAGE: import.meta.env.VITE_OUTPUT_LANGUAGE || 'zh-CN', // zh-CN | en-US + OUTPUT_LANGUAGE: runtimeConfig?.outputLanguage || import.meta.env.VITE_OUTPUT_LANGUAGE || 'zh-CN', // zh-CN | en-US // ==================== 开发环境标识 ==================== isDev: import.meta.env.DEV,