mirror of
https://github.com/tiennm99/ccs.git
synced 2026-09-10 06:20:13 +00:00
fix(api): add try-catch error handling to route handlers
- Add try-catch to cliproxy-auth-routes GET / - Add try-catch to health-routes GET / and POST /fix/:checkId - Add try-catch to config-routes GET /format and POST /migrate - Improve error messages with specific context - All handlers now return 500 with error message on failure
This commit is contained in:
@@ -32,57 +32,61 @@ const validProviders: CLIProxyProvider[] = ['gemini', 'codex', 'agy', 'qwen', 'i
|
|||||||
* GET /api/cliproxy/auth - Get auth status for built-in CLIProxy profiles
|
* GET /api/cliproxy/auth - Get auth status for built-in CLIProxy profiles
|
||||||
* Also fetches CLIProxyAPI stats to update lastUsedAt for active providers
|
* Also fetches CLIProxyAPI stats to update lastUsedAt for active providers
|
||||||
*/
|
*/
|
||||||
router.get('/', async (_req: Request, res: Response) => {
|
router.get('/', async (_req: Request, res: Response): Promise<void> => {
|
||||||
// Initialize accounts from existing tokens on first request
|
try {
|
||||||
initializeAccounts();
|
// Initialize accounts from existing tokens on first request
|
||||||
|
initializeAccounts();
|
||||||
|
|
||||||
// Fetch CLIProxyAPI usage stats to determine active providers
|
// Fetch CLIProxyAPI usage stats to determine active providers
|
||||||
const stats = await fetchCliproxyStats();
|
const stats = await fetchCliproxyStats();
|
||||||
|
|
||||||
// Map CLIProxyAPI provider names to our internal provider names
|
// Map CLIProxyAPI provider names to our internal provider names
|
||||||
const statsProviderMap: Record<string, CLIProxyProvider> = {
|
const statsProviderMap: Record<string, CLIProxyProvider> = {
|
||||||
gemini: 'gemini',
|
gemini: 'gemini',
|
||||||
antigravity: 'agy',
|
antigravity: 'agy',
|
||||||
codex: 'codex',
|
codex: 'codex',
|
||||||
qwen: 'qwen',
|
qwen: 'qwen',
|
||||||
iflow: 'iflow',
|
iflow: 'iflow',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update lastUsedAt for providers with recent activity
|
// Update lastUsedAt for providers with recent activity
|
||||||
if (stats?.requestsByProvider) {
|
if (stats?.requestsByProvider) {
|
||||||
for (const [statsProvider, requestCount] of Object.entries(stats.requestsByProvider)) {
|
for (const [statsProvider, requestCount] of Object.entries(stats.requestsByProvider)) {
|
||||||
if (requestCount > 0) {
|
if (requestCount > 0) {
|
||||||
const provider = statsProviderMap[statsProvider.toLowerCase()];
|
const provider = statsProviderMap[statsProvider.toLowerCase()];
|
||||||
if (provider) {
|
if (provider) {
|
||||||
// Touch the default account for this provider (or all accounts)
|
// Touch the default account for this provider (or all accounts)
|
||||||
const accounts = getProviderAccounts(provider);
|
const accounts = getProviderAccounts(provider);
|
||||||
for (const account of accounts) {
|
for (const account of accounts) {
|
||||||
// Only touch if this is the default account (most likely being used)
|
// Only touch if this is the default account (most likely being used)
|
||||||
if (account.isDefault) {
|
if (account.isDefault) {
|
||||||
touchAccount(provider, account.id);
|
touchAccount(provider, account.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const statuses = getAllAuthStatus();
|
||||||
|
|
||||||
|
const authStatus = statuses.map((status) => {
|
||||||
|
const oauthConfig = getOAuthConfig(status.provider);
|
||||||
|
return {
|
||||||
|
provider: status.provider,
|
||||||
|
displayName: oauthConfig.displayName,
|
||||||
|
authenticated: status.authenticated,
|
||||||
|
lastAuth: status.lastAuth?.toISOString() || null,
|
||||||
|
tokenFiles: status.tokenFiles.length,
|
||||||
|
accounts: status.accounts,
|
||||||
|
defaultAccount: status.defaultAccount,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json({ authStatus });
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: (error as Error).message });
|
||||||
}
|
}
|
||||||
|
|
||||||
const statuses = getAllAuthStatus();
|
|
||||||
|
|
||||||
const authStatus = statuses.map((status) => {
|
|
||||||
const oauthConfig = getOAuthConfig(status.provider);
|
|
||||||
return {
|
|
||||||
provider: status.provider,
|
|
||||||
displayName: oauthConfig.displayName,
|
|
||||||
authenticated: status.authenticated,
|
|
||||||
lastAuth: status.lastAuth?.toISOString() || null,
|
|
||||||
tokenFiles: status.tokenFiles.length,
|
|
||||||
accounts: status.accounts,
|
|
||||||
defaultAccount: status.defaultAccount,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
res.json({ authStatus });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// ==================== Account Management ====================
|
// ==================== Account Management ====================
|
||||||
@@ -91,11 +95,16 @@ router.get('/', async (_req: Request, res: Response) => {
|
|||||||
* GET /api/cliproxy/accounts - Get all accounts across all providers
|
* GET /api/cliproxy/accounts - Get all accounts across all providers
|
||||||
*/
|
*/
|
||||||
router.get('/accounts', (_req: Request, res: Response) => {
|
router.get('/accounts', (_req: Request, res: Response) => {
|
||||||
// Initialize accounts from existing tokens
|
try {
|
||||||
initializeAccounts();
|
// Initialize accounts from existing tokens
|
||||||
|
initializeAccounts();
|
||||||
|
|
||||||
const accounts = getAllAccountsSummary();
|
const accounts = getAllAccountsSummary();
|
||||||
res.json({ accounts });
|
res.json({ accounts });
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : 'Failed to list accounts';
|
||||||
|
res.status(500).json({ error: message });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -110,8 +119,13 @@ router.get('/accounts/:provider', (req: Request, res: Response): void => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const accounts = getProviderAccounts(provider as CLIProxyProvider);
|
try {
|
||||||
res.json({ provider, accounts });
|
const accounts = getProviderAccounts(provider as CLIProxyProvider);
|
||||||
|
res.json({ provider, accounts });
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : 'Failed to get provider accounts';
|
||||||
|
res.status(500).json({ error: message });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -132,12 +146,19 @@ router.post('/accounts/:provider/default', (req: Request, res: Response): void =
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const success = setDefaultAccountFn(provider as CLIProxyProvider, accountId);
|
try {
|
||||||
|
const success = setDefaultAccountFn(provider as CLIProxyProvider, accountId);
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
res.json({ provider, defaultAccount: accountId });
|
res.json({ provider, defaultAccount: accountId });
|
||||||
} else {
|
} else {
|
||||||
res.status(404).json({ error: 'Account not found' });
|
res
|
||||||
|
.status(404)
|
||||||
|
.json({ error: `Account '${accountId}' not found for provider '${provider}'` });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : 'Failed to set default account';
|
||||||
|
res.status(500).json({ error: message });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -153,12 +174,19 @@ router.delete('/accounts/:provider/:accountId', (req: Request, res: Response): v
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const success = removeAccountFn(provider as CLIProxyProvider, accountId);
|
try {
|
||||||
|
const success = removeAccountFn(provider as CLIProxyProvider, accountId);
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
res.json({ provider, accountId, deleted: true });
|
res.json({ provider, accountId, deleted: true });
|
||||||
} else {
|
} else {
|
||||||
res.status(404).json({ error: 'Account not found' });
|
res
|
||||||
|
.status(404)
|
||||||
|
.json({ error: `Account '${accountId}' not found for provider '${provider}'` });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : 'Failed to remove account';
|
||||||
|
res.status(500).json({ error: message });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -24,12 +24,16 @@ const router = Router();
|
|||||||
/**
|
/**
|
||||||
* GET /api/config/format - Return current config format and migration status
|
* GET /api/config/format - Return current config format and migration status
|
||||||
*/
|
*/
|
||||||
router.get('/format', (_req: Request, res: Response) => {
|
router.get('/format', (_req: Request, res: Response): void => {
|
||||||
res.json({
|
try {
|
||||||
format: getConfigFormat(),
|
res.json({
|
||||||
migrationNeeded: needsMigration(),
|
format: getConfigFormat(),
|
||||||
backups: getBackupDirectories(),
|
migrationNeeded: needsMigration(),
|
||||||
});
|
backups: getBackupDirectories(),
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: (error as Error).message });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -89,10 +93,14 @@ router.put('/', (req: Request, res: Response): void => {
|
|||||||
/**
|
/**
|
||||||
* POST /api/config/migrate - Trigger migration from JSON to YAML
|
* POST /api/config/migrate - Trigger migration from JSON to YAML
|
||||||
*/
|
*/
|
||||||
router.post('/migrate', async (req: Request, res: Response) => {
|
router.post('/migrate', async (req: Request, res: Response): Promise<void> => {
|
||||||
const dryRun = req.query.dryRun === 'true';
|
try {
|
||||||
const result = await migrate(dryRun);
|
const dryRun = req.query.dryRun === 'true';
|
||||||
res.json(result);
|
const result = await migrate(dryRun);
|
||||||
|
res.json(result);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: (error as Error).message });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -10,22 +10,30 @@ const router = Router();
|
|||||||
/**
|
/**
|
||||||
* GET /api/health - Run health checks
|
* GET /api/health - Run health checks
|
||||||
*/
|
*/
|
||||||
router.get('/', async (_req: Request, res: Response) => {
|
router.get('/', async (_req: Request, res: Response): Promise<void> => {
|
||||||
const report = await runHealthChecks();
|
try {
|
||||||
res.json(report);
|
const report = await runHealthChecks();
|
||||||
|
res.json(report);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: (error as Error).message });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* POST /api/health/fix/:checkId - Fix a health issue
|
* POST /api/health/fix/:checkId - Fix a health issue
|
||||||
*/
|
*/
|
||||||
router.post('/fix/:checkId', (req: Request, res: Response): void => {
|
router.post('/fix/:checkId', (req: Request, res: Response): void => {
|
||||||
const { checkId } = req.params;
|
try {
|
||||||
const result = fixHealthIssue(checkId);
|
const { checkId } = req.params;
|
||||||
|
const result = fixHealthIssue(checkId);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
res.json({ success: true, message: result.message });
|
res.json({ success: true, message: result.message });
|
||||||
} else {
|
} else {
|
||||||
res.status(400).json({ success: false, message: result.message });
|
res.status(400).json({ success: false, message: result.message });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: (error as Error).message });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user