feat(frontend): implement comprehensive API Settings Management Interface

Implement a complete API Management Interface for React frontend with split-
panel layout, migrating all features from legacy JS frontend.

New Features:
- API Settings page with 5 tabs: Providers, Endpoints, Cache, Model Pools, CLI Settings
- Provider Management: CRUD operations, multi-key rotation, health checks, test connection
- Endpoint Management: CRUD operations, cache strategy configuration, enable/disable toggle
- Cache Settings: Global configuration, statistics display, clear cache functionality
- Model Pool Management: CRUD operations, auto-discovery feature, provider exclusion
- CLI Settings Management: Provider-based and Direct modes, full CRUD support
- Multi-Key Settings Modal: Manage API keys with rotation strategies and weights
- Manage Models Modal: View and manage models per provider (LLM and Embedding)
- Sync to CodexLens: Integration handler for provider configuration sync

Technical Implementation:
- Created 12 new React components in components/api-settings/
- Extended lib/api.ts with 460+ lines of API client functions
- Created hooks/useApiSettings.ts with 772 lines of TanStack Query hooks
- Added RadioGroup UI component for form selections
- Implemented unified error handling with useNotifications across all operations
- Complete i18n support (500+ keys in English and Chinese)
- Route integration (/api-settings) and sidebar navigation

Code Quality:
- All acceptance criteria from plan.json verified
- Code review passed with Gemini (all 7 IMPL tasks complete)
- Follows existing patterns: Shadcn UI, TanStack Query, react-intl, Lucide icons
This commit is contained in:
catlog22
2026-02-01 23:58:04 +08:00
parent 690597bae8
commit abce912ee5
33 changed files with 5874 additions and 45 deletions

View File

@@ -82,6 +82,7 @@ export interface NativeSessionMapping {
native_session_id: string; // Native UUID
native_session_path?: string; // Native file path
project_hash?: string; // Project hash (Gemini/Qwen)
transaction_id?: string; // Transaction ID for concurrent session disambiguation
created_at: string;
}
@@ -360,6 +361,23 @@ export class CliHistoryStore {
console.log('[CLI History] Migration complete: turns table updated');
}
// Add transaction_id column to native_session_mapping table for concurrent session disambiguation
const mappingInfo = this.db.prepare('PRAGMA table_info(native_session_mapping)').all() as Array<{ name: string }>;
const hasTransactionId = mappingInfo.some(col => col.name === 'transaction_id');
if (!hasTransactionId) {
console.log('[CLI History] Migrating database: adding transaction_id column to native_session_mapping...');
this.db.exec(`
ALTER TABLE native_session_mapping ADD COLUMN transaction_id TEXT;
`);
try {
this.db.exec(`CREATE INDEX IF NOT EXISTS idx_native_transaction_id ON native_session_mapping(transaction_id);`);
} catch (indexErr) {
console.warn('[CLI History] Transaction ID index creation warning:', (indexErr as Error).message);
}
console.log('[CLI History] Migration complete: transaction_id column added');
}
} catch (err) {
console.error('[CLI History] Migration error:', (err as Error).message);
// Don't throw - allow the store to continue working with existing schema
@@ -926,12 +944,13 @@ export class CliHistoryStore {
*/
saveNativeSessionMapping(mapping: NativeSessionMapping): void {
const stmt = this.db.prepare(`
INSERT INTO native_session_mapping (ccw_id, tool, native_session_id, native_session_path, project_hash, created_at)
VALUES (@ccw_id, @tool, @native_session_id, @native_session_path, @project_hash, @created_at)
INSERT INTO native_session_mapping (ccw_id, tool, native_session_id, native_session_path, project_hash, transaction_id, created_at)
VALUES (@ccw_id, @tool, @native_session_id, @native_session_path, @project_hash, @transaction_id, @created_at)
ON CONFLICT(ccw_id) DO UPDATE SET
native_session_id = @native_session_id,
native_session_path = @native_session_path,
project_hash = @project_hash
project_hash = @project_hash,
transaction_id = @transaction_id
`);
this.withRetry(() => stmt.run({
@@ -940,6 +959,7 @@ export class CliHistoryStore {
native_session_id: mapping.native_session_id,
native_session_path: mapping.native_session_path || null,
project_hash: mapping.project_hash || null,
transaction_id: mapping.transaction_id || null,
created_at: mapping.created_at || new Date().toISOString()
}));
}
@@ -964,6 +984,16 @@ export class CliHistoryStore {
return row?.ccw_id || null;
}
/**
* Get transaction ID by CCW ID
*/
getTransactionId(ccwId: string): string | null {
const row = this.db.prepare(`
SELECT transaction_id FROM native_session_mapping WHERE ccw_id = ?
`).get(ccwId) as any;
return row?.transaction_id || null;
}
/**
* Get full mapping by CCW ID
*/