mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-17 08:17:22 +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 { Router, Request, Response } from 'express';
|
||||||
import ProfileRegistry from '../../auth/profile-registry';
|
import ProfileRegistry from '../../auth/profile-registry';
|
||||||
import { isUnifiedMode } from '../../config/unified-config-loader';
|
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 router = Router();
|
||||||
const registry = new ProfileRegistry();
|
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
|
* 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 legacyProfiles = registry.getAllProfiles();
|
||||||
const unifiedAccounts = registry.getAllAccountsUnified();
|
const unifiedAccounts = registry.getAllAccountsUnified();
|
||||||
|
|
||||||
|
// Get CLIProxy OAuth accounts (gemini, codex, agy, etc.)
|
||||||
|
const cliproxyAccounts = getAllAccountsSummary();
|
||||||
|
|
||||||
// Merge profiles: unified config takes precedence
|
// 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
|
// Add legacy profiles first
|
||||||
for (const [name, meta] of Object.entries(legacyProfiles)) {
|
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
|
// Convert to array format
|
||||||
const accounts = Object.entries(merged).map(([name, meta]) => ({
|
const accounts = Object.entries(merged).map(([name, meta]) => ({
|
||||||
name,
|
name,
|
||||||
@@ -69,6 +120,18 @@ router.post('/default', (req: Request, res: Response): void => {
|
|||||||
return;
|
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
|
// Use unified config if in unified mode, otherwise use legacy
|
||||||
if (isUnifiedMode()) {
|
if (isUnifiedMode()) {
|
||||||
registry.setDefaultUnified(name);
|
registry.setDefaultUnified(name);
|
||||||
@@ -110,7 +173,7 @@ router.delete('/:name', (req: Request, res: Response): void => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if trying to delete default
|
// Check if trying to delete default (for non-CLIProxy accounts)
|
||||||
const currentDefault = registry.getDefaultUnified() ?? registry.getDefaultProfile();
|
const currentDefault = registry.getDefaultUnified() ?? registry.getDefaultProfile();
|
||||||
if (name === currentDefault) {
|
if (name === currentDefault) {
|
||||||
res
|
res
|
||||||
@@ -119,7 +182,19 @@ router.delete('/:name', (req: Request, res: Response): void => {
|
|||||||
return;
|
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);
|
registry.deleteProfile(name);
|
||||||
|
|
||||||
res.json({ success: true, deleted: name });
|
res.json({ success: true, deleted: name });
|
||||||
|
|||||||
Reference in New Issue
Block a user