feat: add dedicated API key URL support for third-party providers

- Add optional apiKeyUrl field to ProviderPreset interface for third-party providers
- Update ProviderForm to prioritize apiKeyUrl over websiteUrl for third-party category
- Make provider display name required in CodexConfigEditor with validation
- Configure PackyCode preset with affiliate API key URL

This allows third-party providers to have separate URLs for their service homepage
and API key acquisition, improving user experience when obtaining API keys.
This commit is contained in:
Jason
2025-09-21 23:09:53 +08:00
parent 86ef7afbdf
commit 7eaf284400
3 changed files with 24 additions and 5 deletions

View File

@@ -847,7 +847,12 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
// 获取当前供应商的网址 // 获取当前供应商的网址
const getCurrentWebsiteUrl = () => { const getCurrentWebsiteUrl = () => {
if (selectedPreset !== null && selectedPreset >= 0) { if (selectedPreset !== null && selectedPreset >= 0) {
return providerPresets[selectedPreset]?.websiteUrl || ""; const preset = providerPresets[selectedPreset];
if (!preset) return "";
// 仅第三方供应商使用专用 apiKeyUrl其余使用官网地址
return preset.category === "third_party"
? preset.apiKeyUrl || preset.websiteUrl || ""
: preset.websiteUrl || "";
} }
return formData.websiteUrl || ""; return formData.websiteUrl || "";
}; };
@@ -855,7 +860,12 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
// 获取 Codex 当前供应商的网址 // 获取 Codex 当前供应商的网址
const getCurrentCodexWebsiteUrl = () => { const getCurrentCodexWebsiteUrl = () => {
if (selectedCodexPreset !== null && selectedCodexPreset >= 0) { if (selectedCodexPreset !== null && selectedCodexPreset >= 0) {
return codexProviderPresets[selectedCodexPreset]?.websiteUrl || ""; const preset = codexProviderPresets[selectedCodexPreset];
if (!preset) return "";
// 仅第三方供应商使用专用 apiKeyUrl其余使用官网地址
return preset.category === "third_party"
? preset.apiKeyUrl || preset.websiteUrl || ""
: preset.websiteUrl || "";
} }
return formData.websiteUrl || ""; return formData.websiteUrl || "";
}; };
@@ -884,7 +894,7 @@ const ProviderForm: React.FC<ProviderFormProps> = ({
codexProviderPresets[selectedCodexPreset]?.category === "official")) || codexProviderPresets[selectedCodexPreset]?.category === "official")) ||
category === "official"; category === "official";
// 判断是否显示 Codex 的"获取 API Key"链接 // 判断是否显示 Codex 的"获取 API Key"链接(国产官方、聚合站和第三方显示)
const shouldShowCodexApiKeyLink = const shouldShowCodexApiKeyLink =
isCodex && isCodex &&
!isCodexOfficialPreset && !isCodexOfficialPreset &&

View File

@@ -101,6 +101,7 @@ const CodexConfigEditor: React.FC<CodexConfigEditorProps> = ({
const baseUrlInputRef = useRef<HTMLInputElement>(null); const baseUrlInputRef = useRef<HTMLInputElement>(null);
const modelNameInputRef = useRef<HTMLInputElement>(null); const modelNameInputRef = useRef<HTMLInputElement>(null);
const displayNameInputRef = useRef<HTMLInputElement>(null);
// 移除自动填充逻辑,因为现在在点击自定义按钮时就已经填充 // 移除自动填充逻辑,因为现在在点击自定义按钮时就已经填充
@@ -140,6 +141,7 @@ const CodexConfigEditor: React.FC<CodexConfigEditorProps> = ({
const applyTemplate = () => { const applyTemplate = () => {
const requiredInputs = [ const requiredInputs = [
displayNameInputRef.current,
apiKeyInputRef.current, apiKeyInputRef.current,
baseUrlInputRef.current, baseUrlInputRef.current,
modelNameInputRef.current, modelNameInputRef.current,
@@ -389,12 +391,13 @@ const CodexConfigEditor: React.FC<CodexConfigEditorProps> = ({
<div> <div>
<label className="mb-1 block text-sm font-medium text-gray-900 dark:text-gray-100"> <label className="mb-1 block text-sm font-medium text-gray-900 dark:text-gray-100">
*
</label> </label>
<input <input
type="text" type="text"
value={templateDisplayName} value={templateDisplayName}
ref={displayNameInputRef}
onChange={(e) => { onChange={(e) => {
setTemplateDisplayName(e.target.value); setTemplateDisplayName(e.target.value);
if (onNameChange) { if (onNameChange) {
@@ -402,7 +405,10 @@ const CodexConfigEditor: React.FC<CodexConfigEditorProps> = ({
} }
}} }}
onKeyDown={handleTemplateInputKeyDown} onKeyDown={handleTemplateInputKeyDown}
placeholder="例如Codex 官方(可选)" placeholder="例如Codex 官方"
required
pattern=".*\\S.*"
title="请输入有效的内容"
className="w-full rounded-lg border border-gray-200 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500/20 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100" className="w-full rounded-lg border border-gray-200 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500/20 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100"
/> />

View File

@@ -6,6 +6,8 @@ import { ProviderCategory } from "../types";
export interface ProviderPreset { export interface ProviderPreset {
name: string; name: string;
websiteUrl: string; websiteUrl: string;
// 新增:第三方/聚合等可单独配置获取 API Key 的链接
apiKeyUrl?: string;
settingsConfig: object; settingsConfig: object;
isOfficial?: boolean; // 标识是否为官方预设 isOfficial?: boolean; // 标识是否为官方预设
category?: ProviderCategory; // 新增:分类 category?: ProviderCategory; // 新增:分类
@@ -90,6 +92,7 @@ export const providerPresets: ProviderPreset[] = [
{ {
name: "PackyCode", name: "PackyCode",
websiteUrl: "https://www.packycode.com", websiteUrl: "https://www.packycode.com",
apiKeyUrl: "https://www.packycode.com/?aff=rlo54mgz",
settingsConfig: { settingsConfig: {
env: { env: {
ANTHROPIC_BASE_URL: "https://api.packycode.com", ANTHROPIC_BASE_URL: "https://api.packycode.com",