mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-13 18:15:23 +00:00
fix(api): resolve route path mismatches
- Add /usage alias for /cliproxy/stats (frontend compatibility) - Create account-routes.ts to fix /accounts/accounts double path - Update index.ts to mount accountRoutes at /accounts - Remove account code from profile-routes.ts - Clean up unused imports in profile-routes.ts
This commit is contained in:
1 parent
e84df00740
commit
557926ffe3
4 files changed
+99
-66
No files matched your search
@@ -0,0 +1,68 @@
|
|||||||
|
/**
|
||||||
|
* Account Routes - CRUD operations for Claude accounts (profiles.json)
|
||||||
|
*
|
||||||
|
* Separated from profile-routes.ts to avoid dual-mounting conflicts.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Router, Request, Response } from 'express';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
import { getCcsDir } from '../../utils/config-manager';
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /api/accounts - List accounts from profiles.json
|
||||||
|
*/
|
||||||
|
router.get('/', (_req: Request, res: Response): void => {
|
||||||
|
try {
|
||||||
|
const profilesPath = path.join(getCcsDir(), 'profiles.json');
|
||||||
|
|
||||||
|
if (!fs.existsSync(profilesPath)) {
|
||||||
|
res.json({ accounts: [], default: null });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = JSON.parse(fs.readFileSync(profilesPath, 'utf8'));
|
||||||
|
const accounts = Object.entries(data.profiles || {}).map(([name, meta]) => {
|
||||||
|
const metadata = meta as Record<string, unknown>;
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
...metadata,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json({ accounts, default: data.default || null });
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: (error as Error).message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST /api/accounts/default - Set default account
|
||||||
|
*/
|
||||||
|
router.post('/default', (req: Request, res: Response): void => {
|
||||||
|
try {
|
||||||
|
const { name } = req.body;
|
||||||
|
|
||||||
|
if (!name) {
|
||||||
|
res.status(400).json({ error: 'Missing required field: name' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const profilesPath = path.join(getCcsDir(), 'profiles.json');
|
||||||
|
|
||||||
|
const data = fs.existsSync(profilesPath)
|
||||||
|
? JSON.parse(fs.readFileSync(profilesPath, 'utf8'))
|
||||||
|
: { profiles: {} };
|
||||||
|
|
||||||
|
data.default = name;
|
||||||
|
fs.writeFileSync(profilesPath, JSON.stringify(data, null, 2) + '\n');
|
||||||
|
|
||||||
|
res.json({ default: name });
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: (error as Error).message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
@@ -19,10 +19,9 @@ import { checkCliproxyUpdate } from '../../cliproxy/binary-manager';
|
|||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GET /api/cliproxy/stats - Get CLIProxyAPI usage statistics
|
* Shared handler for stats/usage endpoint
|
||||||
* Returns: CliproxyStats or error if proxy not running
|
|
||||||
*/
|
*/
|
||||||
router.get('/stats', async (_req: Request, res: Response): Promise<void> => {
|
const handleStatsRequest = async (_req: Request, res: Response): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
// Check if proxy is running first
|
// Check if proxy is running first
|
||||||
const running = await isCliproxyRunning();
|
const running = await isCliproxyRunning();
|
||||||
@@ -48,7 +47,18 @@ router.get('/stats', async (_req: Request, res: Response): Promise<void> => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ error: (error as Error).message });
|
res.status(500).json({ error: (error as Error).message });
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /api/cliproxy/stats - Get CLIProxyAPI usage statistics
|
||||||
|
* Returns: CliproxyStats or error if proxy not running
|
||||||
|
*/
|
||||||
|
router.get('/stats', handleStatsRequest);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /api/cliproxy/usage - Alias for /stats (frontend compatibility)
|
||||||
|
*/
|
||||||
|
router.get('/usage', handleStatsRequest);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GET /api/cliproxy/status - Check CLIProxyAPI running status
|
* GET /api/cliproxy/status - Check CLIProxyAPI running status
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { Router } from 'express';
|
|||||||
|
|
||||||
// Import domain routers
|
// Import domain routers
|
||||||
import profileRoutes from './profile-routes';
|
import profileRoutes from './profile-routes';
|
||||||
|
import accountRoutes from './account-routes';
|
||||||
import configRoutes from './config-routes';
|
import configRoutes from './config-routes';
|
||||||
import healthRoutes from './health-routes';
|
import healthRoutes from './health-routes';
|
||||||
import providerRoutes from './provider-routes';
|
import providerRoutes from './provider-routes';
|
||||||
@@ -28,7 +29,7 @@ export const apiRoutes = Router();
|
|||||||
// Profile CRUD, settings management, presets, accounts
|
// Profile CRUD, settings management, presets, accounts
|
||||||
apiRoutes.use('/profiles', profileRoutes);
|
apiRoutes.use('/profiles', profileRoutes);
|
||||||
apiRoutes.use('/settings', settingsRoutes);
|
apiRoutes.use('/settings', settingsRoutes);
|
||||||
apiRoutes.use('/accounts', profileRoutes);
|
apiRoutes.use('/accounts', accountRoutes);
|
||||||
|
|
||||||
// ==================== Unified Config ====================
|
// ==================== Unified Config ====================
|
||||||
// Config format, migration
|
// Config format, migration
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
/**
|
/**
|
||||||
* Profile Routes - CRUD operations for user profiles and accounts
|
* Profile Routes - CRUD operations for user profiles
|
||||||
*
|
*
|
||||||
* Uses unified config (config.yaml) when available, falls back to legacy (config.json).
|
* Uses unified config (config.yaml) when available, falls back to legacy (config.json).
|
||||||
|
* Note: Account routes have been moved to account-routes.ts
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Router, Request, Response } from 'express';
|
import { Router, Request, Response } from 'express';
|
||||||
import * as fs from 'fs';
|
|
||||||
import * as path from 'path';
|
|
||||||
import { getCcsDir } from '../../utils/config-manager';
|
|
||||||
import { isReservedName, RESERVED_PROFILE_NAMES } from '../../config/reserved-names';
|
import { isReservedName, RESERVED_PROFILE_NAMES } from '../../config/reserved-names';
|
||||||
import { createApiProfile, removeApiProfile } from '../../api/services/profile-writer';
|
import { createApiProfile, removeApiProfile } from '../../api/services/profile-writer';
|
||||||
import { apiProfileExists, listApiProfiles } from '../../api/services/profile-reader';
|
import { apiProfileExists, listApiProfiles } from '../../api/services/profile-reader';
|
||||||
@@ -20,15 +18,19 @@ const router = Router();
|
|||||||
/**
|
/**
|
||||||
* GET /api/profiles - List all profiles
|
* GET /api/profiles - List all profiles
|
||||||
*/
|
*/
|
||||||
router.get('/', (_req: Request, res: Response) => {
|
router.get('/', (_req: Request, res: Response): void => {
|
||||||
const result = listApiProfiles();
|
try {
|
||||||
// Map isConfigured -> configured for UI compatibility
|
const result = listApiProfiles();
|
||||||
const profiles = result.profiles.map((p) => ({
|
// Map isConfigured -> configured for UI compatibility
|
||||||
name: p.name,
|
const profiles = result.profiles.map((p) => ({
|
||||||
settingsPath: p.settingsPath,
|
name: p.name,
|
||||||
configured: p.isConfigured,
|
settingsPath: p.settingsPath,
|
||||||
}));
|
configured: p.isConfigured,
|
||||||
res.json({ profiles });
|
}));
|
||||||
|
res.json({ profiles });
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: (error as Error).message });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -117,52 +119,4 @@ router.delete('/:name', (req: Request, res: Response): void => {
|
|||||||
res.json({ name, deleted: true });
|
res.json({ name, deleted: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
// ==================== Accounts ====================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* GET /api/accounts - List accounts from profiles.json
|
|
||||||
*/
|
|
||||||
router.get('/accounts', (_req: Request, res: Response): void => {
|
|
||||||
const profilesPath = path.join(getCcsDir(), 'profiles.json');
|
|
||||||
|
|
||||||
if (!fs.existsSync(profilesPath)) {
|
|
||||||
res.json({ accounts: [], default: null });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = JSON.parse(fs.readFileSync(profilesPath, 'utf8'));
|
|
||||||
const accounts = Object.entries(data.profiles || {}).map(([name, meta]) => {
|
|
||||||
const metadata = meta as Record<string, unknown>;
|
|
||||||
return {
|
|
||||||
name,
|
|
||||||
...metadata,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
res.json({ accounts, default: data.default || null });
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* POST /api/accounts/default - Set default account
|
|
||||||
*/
|
|
||||||
router.post('/accounts/default', (req: Request, res: Response): void => {
|
|
||||||
const { name } = req.body;
|
|
||||||
|
|
||||||
if (!name) {
|
|
||||||
res.status(400).json({ error: 'Missing required field: name' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const profilesPath = path.join(getCcsDir(), 'profiles.json');
|
|
||||||
|
|
||||||
const data = fs.existsSync(profilesPath)
|
|
||||||
? JSON.parse(fs.readFileSync(profilesPath, 'utf8'))
|
|
||||||
: { profiles: {} };
|
|
||||||
|
|
||||||
data.default = name;
|
|
||||||
fs.writeFileSync(profilesPath, JSON.stringify(data, null, 2) + '\n');
|
|
||||||
|
|
||||||
res.json({ default: name });
|
|
||||||
});
|
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
Reference in new issue
Block a user