mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-17 00:16:46 +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:
@@ -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();
|
||||
|
||||
/**
|
||||
* GET /api/cliproxy/stats - Get CLIProxyAPI usage statistics
|
||||
* Returns: CliproxyStats or error if proxy not running
|
||||
* Shared handler for stats/usage endpoint
|
||||
*/
|
||||
router.get('/stats', async (_req: Request, res: Response): Promise<void> => {
|
||||
const handleStatsRequest = async (_req: Request, res: Response): Promise<void> => {
|
||||
try {
|
||||
// Check if proxy is running first
|
||||
const running = await isCliproxyRunning();
|
||||
@@ -48,7 +47,18 @@ router.get('/stats', async (_req: Request, res: Response): Promise<void> => {
|
||||
} catch (error) {
|
||||
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
|
||||
|
||||
@@ -9,6 +9,7 @@ import { Router } from 'express';
|
||||
|
||||
// Import domain routers
|
||||
import profileRoutes from './profile-routes';
|
||||
import accountRoutes from './account-routes';
|
||||
import configRoutes from './config-routes';
|
||||
import healthRoutes from './health-routes';
|
||||
import providerRoutes from './provider-routes';
|
||||
@@ -28,7 +29,7 @@ export const apiRoutes = Router();
|
||||
// Profile CRUD, settings management, presets, accounts
|
||||
apiRoutes.use('/profiles', profileRoutes);
|
||||
apiRoutes.use('/settings', settingsRoutes);
|
||||
apiRoutes.use('/accounts', profileRoutes);
|
||||
apiRoutes.use('/accounts', accountRoutes);
|
||||
|
||||
// ==================== Unified Config ====================
|
||||
// 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).
|
||||
* Note: Account routes have been moved to account-routes.ts
|
||||
*/
|
||||
|
||||
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 { createApiProfile, removeApiProfile } from '../../api/services/profile-writer';
|
||||
import { apiProfileExists, listApiProfiles } from '../../api/services/profile-reader';
|
||||
@@ -20,15 +18,19 @@ const router = Router();
|
||||
/**
|
||||
* GET /api/profiles - List all profiles
|
||||
*/
|
||||
router.get('/', (_req: Request, res: Response) => {
|
||||
const result = listApiProfiles();
|
||||
// Map isConfigured -> configured for UI compatibility
|
||||
const profiles = result.profiles.map((p) => ({
|
||||
name: p.name,
|
||||
settingsPath: p.settingsPath,
|
||||
configured: p.isConfigured,
|
||||
}));
|
||||
res.json({ profiles });
|
||||
router.get('/', (_req: Request, res: Response): void => {
|
||||
try {
|
||||
const result = listApiProfiles();
|
||||
// Map isConfigured -> configured for UI compatibility
|
||||
const profiles = result.profiles.map((p) => ({
|
||||
name: p.name,
|
||||
settingsPath: p.settingsPath,
|
||||
configured: p.isConfigured,
|
||||
}));
|
||||
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 });
|
||||
});
|
||||
|
||||
// ==================== 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;
|
||||
|
||||
Reference in New Issue
Block a user