mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 12:15:57 +00:00
fix(accounts): integrate CLIProxy OAuth accounts into API endpoint
- Add CLIProxy accounts to GET /api/accounts (was only showing legacy/unified) - Use unique ID for key to prevent collision between accounts with same nickname - Skip accounts with missing identifier - Route set-default to CLIProxy account manager for provider:id format - Route delete to CLIProxy account manager for provider:id format
This commit is contained in:
@@ -8,10 +8,29 @@
|
||||
import { Router, Request, Response } from 'express';
|
||||
import ProfileRegistry from '../../auth/profile-registry';
|
||||
import { isUnifiedMode } from '../../config/unified-config-loader';
|
||||
import {
|
||||
getAllAccountsSummary,
|
||||
setDefaultAccount as setCliproxyDefault,
|
||||
removeAccount as removeCliproxyAccount,
|
||||
} from '../../cliproxy/account-manager';
|
||||
import { CLIProxyProvider } from '../../cliproxy/types';
|
||||
|
||||
const router = Router();
|
||||
const registry = new ProfileRegistry();
|
||||
|
||||
/** Parse CLIProxy account key format: "provider:accountId" */
|
||||
function parseCliproxyKey(key: string): { provider: CLIProxyProvider; accountId: string } | null {
|
||||
const providers: CLIProxyProvider[] = ['gemini', 'codex', 'agy', 'qwen', 'iflow', 'kiro', 'ghcp'];
|
||||
const colonIndex = key.indexOf(':');
|
||||
if (colonIndex === -1) return null;
|
||||
|
||||
const provider = key.slice(0, colonIndex) as CLIProxyProvider;
|
||||
const accountId = key.slice(colonIndex + 1);
|
||||
|
||||
if (!providers.includes(provider) || !accountId) return null;
|
||||
return { provider, accountId };
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/accounts - List accounts from both profiles.json and config.yaml
|
||||
*/
|
||||
@@ -21,8 +40,20 @@ router.get('/', (_req: Request, res: Response): void => {
|
||||
const legacyProfiles = registry.getAllProfiles();
|
||||
const unifiedAccounts = registry.getAllAccountsUnified();
|
||||
|
||||
// Get CLIProxy OAuth accounts (gemini, codex, agy, etc.)
|
||||
const cliproxyAccounts = getAllAccountsSummary();
|
||||
|
||||
// Merge profiles: unified config takes precedence
|
||||
const merged: Record<string, { type: string; created: string; last_used: string | null }> = {};
|
||||
const merged: Record<
|
||||
string,
|
||||
{
|
||||
type: string;
|
||||
created: string;
|
||||
last_used: string | null;
|
||||
provider?: string;
|
||||
displayName?: string;
|
||||
}
|
||||
> = {};
|
||||
|
||||
// Add legacy profiles first
|
||||
for (const [name, meta] of Object.entries(legacyProfiles)) {
|
||||
@@ -42,6 +73,26 @@ router.get('/', (_req: Request, res: Response): void => {
|
||||
};
|
||||
}
|
||||
|
||||
// Add CLIProxy OAuth accounts
|
||||
for (const [provider, accounts] of Object.entries(cliproxyAccounts)) {
|
||||
for (const acct of accounts) {
|
||||
// Skip accounts with no valid identifier
|
||||
if (!acct.id) {
|
||||
continue;
|
||||
}
|
||||
// Use unique ID for key to prevent collisions between accounts with same nickname/email
|
||||
const displayName = acct.nickname || acct.email || acct.id;
|
||||
const key = `${provider}:${acct.id}`;
|
||||
merged[key] = {
|
||||
type: 'cliproxy',
|
||||
provider,
|
||||
displayName,
|
||||
created: acct.createdAt || new Date().toISOString(),
|
||||
last_used: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to array format
|
||||
const accounts = Object.entries(merged).map(([name, meta]) => ({
|
||||
name,
|
||||
@@ -69,6 +120,18 @@ router.post('/default', (req: Request, res: Response): void => {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this is a CLIProxy account (format: "provider:accountId")
|
||||
const cliproxyKey = parseCliproxyKey(name);
|
||||
if (cliproxyKey) {
|
||||
const success = setCliproxyDefault(cliproxyKey.provider, cliproxyKey.accountId);
|
||||
if (!success) {
|
||||
res.status(404).json({ error: `CLIProxy account not found: ${name}` });
|
||||
return;
|
||||
}
|
||||
res.json({ default: name });
|
||||
return;
|
||||
}
|
||||
|
||||
// Use unified config if in unified mode, otherwise use legacy
|
||||
if (isUnifiedMode()) {
|
||||
registry.setDefaultUnified(name);
|
||||
@@ -110,7 +173,7 @@ router.delete('/:name', (req: Request, res: Response): void => {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if trying to delete default
|
||||
// Check if trying to delete default (for non-CLIProxy accounts)
|
||||
const currentDefault = registry.getDefaultUnified() ?? registry.getDefaultProfile();
|
||||
if (name === currentDefault) {
|
||||
res
|
||||
@@ -119,7 +182,19 @@ router.delete('/:name', (req: Request, res: Response): void => {
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete the profile
|
||||
// Check if this is a CLIProxy account (format: "provider:accountId")
|
||||
const cliproxyKey = parseCliproxyKey(name);
|
||||
if (cliproxyKey) {
|
||||
const success = removeCliproxyAccount(cliproxyKey.provider, cliproxyKey.accountId);
|
||||
if (!success) {
|
||||
res.status(404).json({ error: `CLIProxy account not found: ${name}` });
|
||||
return;
|
||||
}
|
||||
res.json({ success: true, deleted: name });
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete the profile (legacy/unified)
|
||||
registry.deleteProfile(name);
|
||||
|
||||
res.json({ success: true, deleted: name });
|
||||
|
||||
Reference in New Issue
Block a user