mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 06:16:37 +00:00
fix: prevent Docker dashboard from orphaning CLIProxy
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
import { describe, expect, it } from 'bun:test';
|
||||
|
||||
import { handleConfigCommand } from '../config-command';
|
||||
|
||||
describe('handleConfigCommand Docker supervisord lifecycle', () => {
|
||||
it('does not spawn CLIProxy directly when supervisord owns the runtime', async () => {
|
||||
let ensureCalled = false;
|
||||
const logs: string[] = [];
|
||||
const originalLog = console.log;
|
||||
|
||||
console.log = (message?: unknown) => {
|
||||
logs.push(String(message ?? ''));
|
||||
};
|
||||
|
||||
try {
|
||||
await handleConfigCommand([], {
|
||||
getPort: async () => 3000,
|
||||
openBrowser: async () => ({}),
|
||||
startServer: async () =>
|
||||
({
|
||||
server: { address: () => ({ address: '0.0.0.0' }) },
|
||||
wss: {},
|
||||
cleanup: () => {},
|
||||
}) as never,
|
||||
setupGracefulShutdown: () => {},
|
||||
ensureCliproxyService: async () => {
|
||||
ensureCalled = true;
|
||||
return { started: false, alreadyRunning: false, port: 8317 };
|
||||
},
|
||||
isRunningUnderSupervisord: () => true,
|
||||
getDashboardAuthConfig: () => ({ enabled: true }),
|
||||
initUI: async () => {},
|
||||
header: (text: string) => text,
|
||||
ok: (text: string) => text,
|
||||
info: (text: string) => text,
|
||||
warn: (text: string) => text,
|
||||
fail: (text: string) => text,
|
||||
resolveNamedCommand: () => undefined,
|
||||
configSubcommandRoutes: [],
|
||||
});
|
||||
} finally {
|
||||
console.log = originalLog;
|
||||
}
|
||||
|
||||
expect(ensureCalled).toBe(false);
|
||||
expect(logs).toContain('CLIProxy is managed by supervisord on port 8317');
|
||||
});
|
||||
});
|
||||
@@ -12,6 +12,7 @@ import { startServer } from '../web-server';
|
||||
import { setupGracefulShutdown } from '../web-server/shutdown';
|
||||
import { ensureCliproxyService } from '../cliproxy/service-manager';
|
||||
import { resolveLifecyclePort } from '../cliproxy/config/port-manager';
|
||||
import { isRunningUnderSupervisord } from '../docker/supervisord-lifecycle';
|
||||
import { initUI, header, ok, info, warn, fail } from '../utils/ui';
|
||||
import { resolveNamedCommand, type NamedCommandRoute } from './named-command-router';
|
||||
import {
|
||||
@@ -63,6 +64,7 @@ interface ConfigCommandDependencies {
|
||||
startServer: typeof startServer;
|
||||
setupGracefulShutdown: typeof setupGracefulShutdown;
|
||||
ensureCliproxyService: typeof ensureCliproxyService;
|
||||
isRunningUnderSupervisord?: typeof isRunningUnderSupervisord;
|
||||
getDashboardAuthConfig: typeof getDashboardAuthConfig;
|
||||
initUI: typeof initUI;
|
||||
header: typeof header;
|
||||
@@ -80,6 +82,7 @@ const defaultConfigCommandDependencies: ConfigCommandDependencies = {
|
||||
startServer,
|
||||
setupGracefulShutdown,
|
||||
ensureCliproxyService,
|
||||
isRunningUnderSupervisord,
|
||||
getDashboardAuthConfig,
|
||||
initUI,
|
||||
header,
|
||||
@@ -135,29 +138,37 @@ export async function handleConfigCommand(
|
||||
console.log(deps.header('CCS Config Dashboard'));
|
||||
console.log('');
|
||||
|
||||
// Ensure CLIProxy service is running for dashboard features
|
||||
console.log(deps.info('Starting CLIProxy service...'));
|
||||
const cliproxyResult = await deps.ensureCliproxyService(resolveLifecyclePort(), verbose);
|
||||
logger.info('cliproxy.ensure_result', 'Config command checked CLIProxy availability', {
|
||||
started: cliproxyResult.started,
|
||||
alreadyRunning: cliproxyResult.alreadyRunning,
|
||||
configRegenerated: cliproxyResult.configRegenerated,
|
||||
port: cliproxyResult.port || null,
|
||||
error: cliproxyResult.error || null,
|
||||
});
|
||||
const lifecyclePort = resolveLifecyclePort();
|
||||
if (deps.isRunningUnderSupervisord?.() ?? false) {
|
||||
logger.info('cliproxy.supervisord_managed', 'Skipping direct CLIProxy startup in Docker', {
|
||||
port: lifecyclePort,
|
||||
});
|
||||
console.log(deps.info(`CLIProxy is managed by supervisord on port ${lifecyclePort}`));
|
||||
} else {
|
||||
// Ensure CLIProxy service is running for dashboard features
|
||||
console.log(deps.info('Starting CLIProxy service...'));
|
||||
const cliproxyResult = await deps.ensureCliproxyService(lifecyclePort, verbose);
|
||||
logger.info('cliproxy.ensure_result', 'Config command checked CLIProxy availability', {
|
||||
started: cliproxyResult.started,
|
||||
alreadyRunning: cliproxyResult.alreadyRunning,
|
||||
configRegenerated: cliproxyResult.configRegenerated,
|
||||
port: cliproxyResult.port || null,
|
||||
error: cliproxyResult.error || null,
|
||||
});
|
||||
|
||||
if (cliproxyResult.started) {
|
||||
if (cliproxyResult.alreadyRunning) {
|
||||
console.log(deps.ok(`CLIProxy already running on port ${cliproxyResult.port}`));
|
||||
if (cliproxyResult.configRegenerated) {
|
||||
console.log(deps.warn('Config updated - restart CLIProxy to apply changes'));
|
||||
if (cliproxyResult.started) {
|
||||
if (cliproxyResult.alreadyRunning) {
|
||||
console.log(deps.ok(`CLIProxy already running on port ${cliproxyResult.port}`));
|
||||
if (cliproxyResult.configRegenerated) {
|
||||
console.log(deps.warn('Config updated - restart CLIProxy to apply changes'));
|
||||
}
|
||||
} else {
|
||||
console.log(deps.ok(`CLIProxy started on port ${cliproxyResult.port}`));
|
||||
}
|
||||
} else {
|
||||
console.log(deps.ok(`CLIProxy started on port ${cliproxyResult.port}`));
|
||||
console.log(deps.warn(`CLIProxy not available: ${cliproxyResult.error}`));
|
||||
console.log(deps.info('Dashboard will work but Control Panel/Stats may be limited'));
|
||||
}
|
||||
} else {
|
||||
console.log(deps.warn(`CLIProxy not available: ${cliproxyResult.error}`));
|
||||
console.log(deps.info('Dashboard will work but Control Panel/Stats may be limited'));
|
||||
}
|
||||
console.log('');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user