Files
CC-Switch/src/lib/api/providers.ts
T
Calcium-Ion 8fe5c1041a feat: add Universal Provider feature (#348)
* feat: add Universal Provider feature

- Add Universal Provider data structures and type definitions
- Implement backend CRUD operations and sync functionality
- Add frontend UI components (UniversalProviderPanel, Card, FormModal)
- Add NewAPI icon and preset configuration
- Support cross-app (Claude/Codex/Gemini) configuration sync
- Add website URL field for providers
- Implement real-time refresh via event notifications
- Add i18n support (Chinese/English/Japanese)

* feat: integrate universal provider presets into add provider dialog

- Add universal provider presets (NewAPI, Custom Gateway) to preset selector
- Show universal presets with Layers icon badge in preset selector
- Open UniversalProviderFormModal when universal preset is clicked
- Pass initialPreset to auto-fill form when opened from add dialog
- Add i18n keys for addSuccess/addFailed messages
- Keep separate universal provider panel for management

* refactor: move universal provider management to add dialog

- Remove Layers button from main navigation header
- Add 'Manage' button next to universal provider presets
- Open UniversalProviderPanel from within add provider dialog
- Add i18n keys for 'manage' in all locales

* style: display universal provider presets on separate line

- Move universal provider section to a new row with border separator
- Add label '统一供应商:' to clarify the section

* style: unify universal provider label style with preset label

- Use FormLabel component for consistent styling
- Add background to 'Manage' button matching preset buttons
- Update icon size and button padding for consistency

* feat: add sync functionality and JSON preview for Universal Provider

* fix: add missing in_failover_queue field to Provider structs

After rebasing to main, the Provider struct gained a new
`in_failover_queue` field. This fix adds the missing field
to the three to_*_provider() methods in UniversalProvider.

* refactor: redesign AddProviderDialog with tab-based layout

- Add tabs to separate app-specific providers and universal providers
- Move "Add Universal Provider" button from panel header to footer
- Remove unused handleAdd callback and clean up imports
- Update emptyHint i18n text to reference the footer button

* fix: append /v1 suffix to Codex base_url in Universal Provider

Codex uses OpenAI-compatible API which requires the /v1 endpoint suffix.
The Universal Provider now automatically appends /v1 to base_url when
generating Codex provider config if not already present.

- Handle trailing slashes to avoid double slashes
- Apply fix to both backend (to_codex_provider) and frontend preview

* feat: auto-sync universal provider to apps on creation

Previously, users had to manually click sync after adding a universal
provider. Now it automatically syncs to Claude/Codex/Gemini on creation,
providing a smoother user experience.

---------

Co-authored-by: Jason <farion1231@gmail.com>
2025-12-26 22:47:24 +08:00

110 lines
2.9 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.
import { invoke } from "@tauri-apps/api/core";
import { listen, type UnlistenFn } from "@tauri-apps/api/event";
import type {
Provider,
UniversalProvider,
UniversalProvidersMap,
} from "@/types";
import type { AppId } from "./types";
export interface ProviderSortUpdate {
id: string;
sortIndex: number;
}
export interface ProviderSwitchEvent {
appType: AppId;
providerId: string;
}
export const providersApi = {
async getAll(appId: AppId): Promise<Record<string, Provider>> {
return await invoke("get_providers", { app: appId });
},
async getCurrent(appId: AppId): Promise<string> {
return await invoke("get_current_provider", { app: appId });
},
async add(provider: Provider, appId: AppId): Promise<boolean> {
return await invoke("add_provider", { provider, app: appId });
},
async update(provider: Provider, appId: AppId): Promise<boolean> {
return await invoke("update_provider", { provider, app: appId });
},
async delete(id: string, appId: AppId): Promise<boolean> {
return await invoke("delete_provider", { id, app: appId });
},
async switch(id: string, appId: AppId): Promise<boolean> {
return await invoke("switch_provider", { id, app: appId });
},
async importDefault(appId: AppId): Promise<boolean> {
return await invoke("import_default_config", { app: appId });
},
async updateTrayMenu(): Promise<boolean> {
return await invoke("update_tray_menu");
},
async updateSortOrder(
updates: ProviderSortUpdate[],
appId: AppId,
): Promise<boolean> {
return await invoke("update_providers_sort_order", { updates, app: appId });
},
async onSwitched(
handler: (event: ProviderSwitchEvent) => void,
): Promise<UnlistenFn> {
return await listen("provider-switched", (event) => {
const payload = event.payload as ProviderSwitchEvent;
handler(payload);
});
},
};
// ============================================================================
// 统一供应商(Universal ProviderAPI
// ============================================================================
export const universalProvidersApi = {
/**
* 获取所有统一供应商
*/
async getAll(): Promise<UniversalProvidersMap> {
return await invoke("get_universal_providers");
},
/**
* 获取单个统一供应商
*/
async get(id: string): Promise<UniversalProvider | null> {
return await invoke("get_universal_provider", { id });
},
/**
* 添加或更新统一供应商
*/
async upsert(provider: UniversalProvider): Promise<boolean> {
return await invoke("upsert_universal_provider", { provider });
},
/**
* 删除统一供应商
*/
async delete(id: string): Promise<boolean> {
return await invoke("delete_universal_provider", { id });
},
/**
* 手动同步统一供应商到各应用
*/
async sync(id: string): Promise<boolean> {
return await invoke("sync_universal_provider", { id });
},
};