mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 10:16:49 +00:00
fix(dashboard): support unified config.yaml in web server routes
Add loadConfigSafe() function that handles both unified (config.yaml) and legacy (config.json) config formats. Uses throwable errors instead of process.exit() so try/catch blocks work properly in web server routes. Updated files to use loadConfigSafe(): - overview-routes.ts: Dashboard overview API - route-helpers.ts: readConfigSafe() helper - profile-reader.ts: API profile reading - profile-writer.ts: API profile writing - variant-config-adapter.ts: CLIProxy variant config Fixes #206 (Problem 2: config.json not found when user has config.yaml)
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { getCcsDir, loadConfig } from '../../utils/config-manager';
|
||||
import { getCcsDir, loadConfigSafe } from '../../utils/config-manager';
|
||||
import { loadOrCreateUnifiedConfig, isUnifiedMode } from '../../config/unified-config-loader';
|
||||
import type { ApiProfileInfo, CliproxyVariantInfo, ApiListResult } from './profile-types';
|
||||
|
||||
@@ -20,7 +20,7 @@ export function apiProfileExists(name: string): boolean {
|
||||
const config = loadOrCreateUnifiedConfig();
|
||||
return name in config.profiles;
|
||||
}
|
||||
const config = loadConfig();
|
||||
const config = loadConfigSafe();
|
||||
return name in config.profiles;
|
||||
} catch {
|
||||
return false;
|
||||
@@ -79,7 +79,7 @@ export function listApiProfiles(): ApiListResult {
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const config = loadConfig();
|
||||
const config = loadConfigSafe();
|
||||
for (const [name, settingsPath] of Object.entries(config.profiles)) {
|
||||
// Skip 'default' profile - it's the user's native Claude settings
|
||||
if (name === 'default' && (settingsPath as string).includes('.claude/settings.json')) {
|
||||
@@ -116,7 +116,7 @@ export function getApiProfileNames(): string[] {
|
||||
const config = loadOrCreateUnifiedConfig();
|
||||
return Object.keys(config.profiles);
|
||||
}
|
||||
const config = loadConfig();
|
||||
const config = loadConfigSafe();
|
||||
return Object.keys(config.profiles);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import { getCcsDir, getConfigPath, loadConfig } from '../../utils/config-manager';
|
||||
import { getCcsDir, getConfigPath, loadConfigSafe } from '../../utils/config-manager';
|
||||
import { expandPath } from '../../utils/helpers';
|
||||
import {
|
||||
loadOrCreateUnifiedConfig,
|
||||
@@ -166,7 +166,7 @@ function removeApiProfileUnified(name: string): void {
|
||||
|
||||
/** Remove API profile from legacy config */
|
||||
function removeApiProfileLegacy(name: string): void {
|
||||
const config = loadConfig();
|
||||
const config = loadConfigSafe();
|
||||
delete config.profiles[name];
|
||||
|
||||
const configPath = getConfigPath();
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import { getConfigPath, loadConfig } from '../../utils/config-manager';
|
||||
import { getConfigPath, loadConfigSafe } from '../../utils/config-manager';
|
||||
import { CLIProxyProvider } from '../types';
|
||||
import {
|
||||
loadOrCreateUnifiedConfig,
|
||||
@@ -38,7 +38,7 @@ export function variantExistsInConfig(name: string): boolean {
|
||||
const config = loadOrCreateUnifiedConfig();
|
||||
return !!(config.cliproxy?.variants && name in config.cliproxy.variants);
|
||||
}
|
||||
const config = loadConfig();
|
||||
const config = loadConfigSafe();
|
||||
return !!(config.cliproxy && name in config.cliproxy);
|
||||
} catch {
|
||||
return false;
|
||||
@@ -94,7 +94,7 @@ export function listVariantsFromConfig(): Record<string, VariantConfig> {
|
||||
return result;
|
||||
}
|
||||
|
||||
const config = loadConfig();
|
||||
const config = loadConfigSafe();
|
||||
const variants = config.cliproxy || {};
|
||||
const result: Record<string, VariantConfig> = {};
|
||||
for (const name of Object.keys(variants)) {
|
||||
|
||||
@@ -12,6 +12,7 @@ export type {
|
||||
EnvValue,
|
||||
ProfileMetadata,
|
||||
ProfilesRegistry,
|
||||
CLIProxyVariantsConfig,
|
||||
} from './config';
|
||||
export { isConfig, isSettings } from './config';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import { Config, isConfig, Settings, isSettings } from '../types';
|
||||
import { Config, isConfig, Settings, isSettings, CLIProxyVariantsConfig } from '../types';
|
||||
import { expandPath, error } from './helpers';
|
||||
import { info } from './ui';
|
||||
import { isUnifiedMode, loadOrCreateUnifiedConfig } from '../config/unified-config-loader';
|
||||
@@ -82,6 +82,63 @@ export function readConfig(): Config {
|
||||
return loadConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load config safely with unified mode support.
|
||||
* Returns Config with profiles from unified config.yaml or legacy config.json.
|
||||
* Throws on error (catchable) instead of process.exit.
|
||||
* Use this in web server routes where try/catch is needed.
|
||||
*/
|
||||
export function loadConfigSafe(): Config {
|
||||
// Unified mode: extract profiles from config.yaml
|
||||
if (isUnifiedMode()) {
|
||||
const unifiedConfig = loadOrCreateUnifiedConfig();
|
||||
|
||||
// Convert unified profiles to legacy format for compatibility
|
||||
const profiles: Record<string, string> = {};
|
||||
for (const [name, profile] of Object.entries(unifiedConfig.profiles)) {
|
||||
if (profile.settings) {
|
||||
profiles[name] = profile.settings;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert unified cliproxy variants to legacy format
|
||||
let cliproxy: CLIProxyVariantsConfig | undefined;
|
||||
if (unifiedConfig.cliproxy?.variants) {
|
||||
cliproxy = {};
|
||||
for (const [name, variant] of Object.entries(unifiedConfig.cliproxy.variants)) {
|
||||
cliproxy[name] = {
|
||||
// Cast provider - unified has more providers than legacy type
|
||||
provider: variant.provider as 'gemini' | 'codex' | 'agy' | 'qwen',
|
||||
settings: variant.settings || '',
|
||||
account: variant.account,
|
||||
port: variant.port,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
profiles,
|
||||
cliproxy,
|
||||
};
|
||||
}
|
||||
|
||||
// Legacy mode: read config.json
|
||||
const configPath = getConfigPath();
|
||||
|
||||
if (!fs.existsSync(configPath)) {
|
||||
throw new Error(`Config not found: ${configPath}`);
|
||||
}
|
||||
|
||||
const raw = fs.readFileSync(configPath, 'utf8');
|
||||
const parsed: unknown = JSON.parse(raw);
|
||||
|
||||
if (!isConfig(parsed)) {
|
||||
throw new Error(`Invalid config format: ${configPath}`);
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get settings path for profile.
|
||||
* In unified mode (config.yaml exists), reads from config.yaml first,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import { Router, Request, Response } from 'express';
|
||||
import { loadConfig } from '../utils/config-manager';
|
||||
import { loadConfigSafe } from '../utils/config-manager';
|
||||
import { runHealthChecks } from './health-service';
|
||||
import { getAllAuthStatus, initializeAccounts } from '../cliproxy/auth-handler';
|
||||
import { getVersion } from '../utils/version';
|
||||
@@ -18,7 +18,7 @@ export const overviewRoutes = Router();
|
||||
*/
|
||||
overviewRoutes.get('/', async (_req: Request, res: Response) => {
|
||||
try {
|
||||
const config = loadConfig();
|
||||
const config = loadConfigSafe();
|
||||
|
||||
const profileCount = Object.keys(config.profiles).length;
|
||||
const cliproxyVariantCount = Object.keys(config.cliproxy || {}).length;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { getCcsDir, getConfigPath, loadConfig, loadSettings } from '../../utils/config-manager';
|
||||
import { getCcsDir, getConfigPath, loadConfigSafe, loadSettings } from '../../utils/config-manager';
|
||||
import { expandPath } from '../../utils/helpers';
|
||||
import type { Config, Settings } from '../../types/config';
|
||||
|
||||
@@ -17,11 +17,12 @@ export interface ModelMapping {
|
||||
}
|
||||
|
||||
/**
|
||||
* Read config safely with fallback
|
||||
* Read config safely with fallback.
|
||||
* Uses loadConfigSafe which supports both unified (config.yaml) and legacy (config.json).
|
||||
*/
|
||||
export function readConfigSafe(): Config {
|
||||
try {
|
||||
return loadConfig();
|
||||
return loadConfigSafe();
|
||||
} catch {
|
||||
return { profiles: {} };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user