Files
cc-switch/src/types.ts
T
杨永安 07d022ba9f feat(usage): improve custom template system with variable hints and validation fixes (#628)
* feat(usage): improve custom template with variables display and explicit type detection

Combine two feature improvements:
1. Display supported variables ({{baseUrl}}, {{apiKey}}) with actual values in custom template mode
2. Add explicit templateType field for accurate template mode detection

## Changes

### Frontend
- Display template variables with actual values extracted from provider settings
- Add templateType field to UsageScript for explicit mode detection
- Support template mode persistence across sessions

### Backend
- Add template_type field to UsageScript struct
- Improve validation logic based on explicit template type
- Maintain backward compatibility with type inference

### I18n
- Add "Supported Variables" section translation (zh/en/ja)

### Benefits
- More accurate template mode detection (no more guessing)
- Better user experience with variable hints
- Clearer validation rules per template type

* fix(usage): resolve custom template cache and validation issues

Combine three bug fixes to make custom template mode work correctly:

1. **Update cache after test**: Testing usage script successfully now updates the main list cache immediately
2. **Fix same-origin check**: Custom template mode can now access different domains (SSRF protection still active)
3. **Fix field naming**: Unified to use autoQueryInterval consistently between frontend and backend

## Problems Solved

- Main provider list showing "Query failed" after successful test
- Custom templates blocked by overly strict same-origin validation
- Auto-query intervals not saved correctly due to inconsistent naming

## Changes

### Frontend (UsageScriptModal)
- Import useQueryClient and update cache after successful test
- Invalidate usage cache when saving script configuration
- Use standardized autoQueryInterval field name

### Backend (usage_script.rs)
- Allow custom template mode to bypass same-origin checks
- Maintain SSRF protection for all modes

### Hooks (useProviderActions)
- Invalidate usage query cache when saving script

## Impact

Users can now use custom templates freely while security validations remain intact for general templates.

* fix(usage): correct provider credential field names

- Claude: support both ANTHROPIC_API_KEY and ANTHROPIC_AUTH_TOKEN
- Gemini: use GEMINI_API_KEY instead of GOOGLE_GEMINI_API_KEY
- Codex: use OPENAI_API_KEY and parse base_url from TOML config string

Addresses review feedback from PR #628

* style: format code

---------

Co-authored-by: Jason <farion1231@gmail.com>
2026-01-14 15:42:05 +08:00

250 lines
7.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
export type ProviderCategory =
| "official" // 官方
| "cn_official" // 开源官方(原"国产官方")
| "aggregator" // 聚合网站
| "third_party" // 第三方供应商
| "custom"; // 自定义
export interface Provider {
id: string;
name: string;
settingsConfig: Record<string, any>; // 应用配置对象:Claude 为 settings.jsonCodex 为 { auth, config }
websiteUrl?: string;
// 新增:供应商分类(用于差异化提示/能力开关)
category?: ProviderCategory;
createdAt?: number; // 添加时间戳(毫秒)
sortIndex?: number; // 排序索引(用于自定义拖拽排序)
// 备注信息
notes?: string;
// 新增:是否为商业合作伙伴
isPartner?: boolean;
// 可选:供应商元数据(仅存于 ~/.cc-switch/config.json,不写入 live 配置)
meta?: ProviderMeta;
// 图标配置
icon?: string; // 图标名称(如 "openai", "anthropic"
iconColor?: string; // 图标颜色(Hex 格式,如 "#00A67E"
// 是否加入故障转移队列
inFailoverQueue?: boolean;
}
export interface AppConfig {
providers: Record<string, Provider>;
current: string;
}
// 自定义端点配置
export interface CustomEndpoint {
url: string;
addedAt: number;
lastUsed?: number;
}
// 端点候选项(用于端点测速弹窗)
export interface EndpointCandidate {
id?: string;
url: string;
isCustom?: boolean;
}
// 用量查询脚本配置
export interface UsageScript {
enabled: boolean; // 是否启用用量查询
language: "javascript"; // 脚本语言
code: string; // 脚本代码(JSON 格式配置)
timeout?: number; // 超时时间(秒,默认 10
templateType?: "custom" | "general" | "newapi"; // 模板类型(用于后端判断验证规则)
apiKey?: string; // 用量查询专用的 API Key(通用模板使用)
baseUrl?: string; // 用量查询专用的 Base URL(通用和 NewAPI 模板使用)
accessToken?: string; // 访问令牌(NewAPI 模板使用)
userId?: string; // 用户IDNewAPI 模板使用)
autoQueryInterval?: number; // 自动查询间隔(单位:分钟,0 表示禁用)
autoIntervalMinutes?: number; // 自动查询间隔(分钟)- 别名字段
request?: {
// 请求配置
url?: string; // 请求 URL
method?: string; // HTTP 方法
headers?: Record<string, string>; // 请求头
body?: any; // 请求体
};
}
// 单个套餐用量数据
export interface UsageData {
planName?: string; // 套餐名称(可选)
extra?: string; // 扩展字段,可自由补充需要展示的文本(可选)
isValid?: boolean; // 套餐是否有效(可选)
invalidMessage?: string; // 失效原因说明(可选,当 isValid 为 false 时显示)
total?: number; // 总额度(可选)
used?: number; // 已用额度(可选)
remaining?: number; // 剩余额度(可选)
unit?: string; // 单位(可选)
}
// 用量查询结果(支持多套餐)
export interface UsageResult {
success: boolean;
data?: UsageData[]; // 改为数组,支持返回多个套餐
error?: string;
}
// 供应商元数据(字段名与后端一致,保持 snake_case
export interface ProviderMeta {
// 自定义端点:以 URL 为键,值为端点信息
custom_endpoints?: Record<string, CustomEndpoint>;
// 用量查询脚本配置
usage_script?: UsageScript;
// 请求地址管理:测速后自动选择最佳端点
endpointAutoSelect?: boolean;
// 是否为官方合作伙伴
isPartner?: boolean;
// 合作伙伴促销 key(用于后端识别 PackyCode 等)
partnerPromotionKey?: string;
}
// 应用设置类型(用于设置对话框与 Tauri API)
// 存储在本地 ~/.cc-switch/settings.json,不随数据库同步
export interface Settings {
// ===== 设备级 UI 设置 =====
// 是否在系统托盘(macOS 菜单栏)显示图标
showInTray: boolean;
// 点击关闭按钮时是否最小化到托盘而不是关闭应用
minimizeToTrayOnClose: boolean;
// 启用 Claude 插件联动(写入 ~/.claude/config.json 的 primaryApiKey
enableClaudePluginIntegration?: boolean;
// 跳过 Claude Code 初次安装确认(写入 ~/.claude.json 的 hasCompletedOnboarding
skipClaudeOnboarding?: boolean;
// 是否开机自启
launchOnStartup?: boolean;
// 首选语言(可选,默认中文)
language?: "en" | "zh" | "ja";
// ===== 设备级目录覆盖 =====
// 覆盖 Claude Code 配置目录(可选)
claudeConfigDir?: string;
// 覆盖 Codex 配置目录(可选)
codexConfigDir?: string;
// 覆盖 Gemini 配置目录(可选)
geminiConfigDir?: string;
// ===== 当前供应商 ID(设备级)=====
// 当前 Claude 供应商 ID(优先于数据库 is_current
currentProviderClaude?: string;
// 当前 Codex 供应商 ID(优先于数据库 is_current
currentProviderCodex?: string;
// 当前 Gemini 供应商 ID(优先于数据库 is_current
currentProviderGemini?: string;
}
// MCP 服务器连接参数(宽松:允许扩展字段)
export interface McpServerSpec {
// 可选:社区常见 .mcp.json 中 stdio 配置可不写 type
type?: "stdio" | "http" | "sse";
// stdio 字段
command?: string;
args?: string[];
env?: Record<string, string>;
cwd?: string;
// http 和 sse 字段
url?: string;
headers?: Record<string, string>;
// 通用字段
[key: string]: any;
}
// v3.7.0: MCP 服务器应用启用状态
export interface McpApps {
claude: boolean;
codex: boolean;
gemini: boolean;
}
// MCP 服务器条目(v3.7.0 统一结构)
export interface McpServer {
id: string;
name: string;
server: McpServerSpec;
apps: McpApps; // v3.7.0: 标记应用到哪些客户端
description?: string;
tags?: string[];
homepage?: string;
docs?: string;
// 兼容旧字段(v3.6.x 及以前)
enabled?: boolean; // 已废弃,v3.7.0 使用 apps 字段
source?: string;
[key: string]: any;
}
// MCP 服务器映射(id -> McpServer
export type McpServersMap = Record<string, McpServer>;
// MCP 配置状态
export interface McpStatus {
userConfigPath: string;
userConfigExists: boolean;
serverCount: number;
}
// 新:来自 config.json 的 MCP 列表响应
export interface McpConfigResponse {
configPath: string;
servers: Record<string, McpServer>;
}
// ============================================================================
// 统一供应商(Universal Provider- 跨应用共享配置
// ============================================================================
// 统一供应商的应用启用状态
export interface UniversalProviderApps {
claude: boolean;
codex: boolean;
gemini: boolean;
}
// Claude 模型配置
export interface ClaudeModelConfig {
model?: string;
haikuModel?: string;
sonnetModel?: string;
opusModel?: string;
}
// Codex 模型配置
export interface CodexModelConfig {
model?: string;
reasoningEffort?: string;
}
// Gemini 模型配置
export interface GeminiModelConfig {
model?: string;
}
// 各应用的模型配置
export interface UniversalProviderModels {
claude?: ClaudeModelConfig;
codex?: CodexModelConfig;
gemini?: GeminiModelConfig;
}
// 统一供应商(跨应用共享配置)
export interface UniversalProvider {
id: string;
name: string;
providerType: string; // "newapi" | "custom" 等
apps: UniversalProviderApps;
baseUrl: string;
apiKey: string;
models: UniversalProviderModels;
websiteUrl?: string;
notes?: string;
icon?: string;
iconColor?: string;
meta?: ProviderMeta;
createdAt?: number;
sortIndex?: number;
}
// 统一供应商映射(id -> UniversalProvider
export type UniversalProvidersMap = Record<string, UniversalProvider>;