mirror of
https://github.com/tiennm99/ccs.git
synced 2026-09-02 18:18:43 +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 { setupGracefulShutdown } from '../web-server/shutdown';
|
||||||
import { ensureCliproxyService } from '../cliproxy/service-manager';
|
import { ensureCliproxyService } from '../cliproxy/service-manager';
|
||||||
import { resolveLifecyclePort } from '../cliproxy/config/port-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 { initUI, header, ok, info, warn, fail } from '../utils/ui';
|
||||||
import { resolveNamedCommand, type NamedCommandRoute } from './named-command-router';
|
import { resolveNamedCommand, type NamedCommandRoute } from './named-command-router';
|
||||||
import {
|
import {
|
||||||
@@ -63,6 +64,7 @@ interface ConfigCommandDependencies {
|
|||||||
startServer: typeof startServer;
|
startServer: typeof startServer;
|
||||||
setupGracefulShutdown: typeof setupGracefulShutdown;
|
setupGracefulShutdown: typeof setupGracefulShutdown;
|
||||||
ensureCliproxyService: typeof ensureCliproxyService;
|
ensureCliproxyService: typeof ensureCliproxyService;
|
||||||
|
isRunningUnderSupervisord?: typeof isRunningUnderSupervisord;
|
||||||
getDashboardAuthConfig: typeof getDashboardAuthConfig;
|
getDashboardAuthConfig: typeof getDashboardAuthConfig;
|
||||||
initUI: typeof initUI;
|
initUI: typeof initUI;
|
||||||
header: typeof header;
|
header: typeof header;
|
||||||
@@ -80,6 +82,7 @@ const defaultConfigCommandDependencies: ConfigCommandDependencies = {
|
|||||||
startServer,
|
startServer,
|
||||||
setupGracefulShutdown,
|
setupGracefulShutdown,
|
||||||
ensureCliproxyService,
|
ensureCliproxyService,
|
||||||
|
isRunningUnderSupervisord,
|
||||||
getDashboardAuthConfig,
|
getDashboardAuthConfig,
|
||||||
initUI,
|
initUI,
|
||||||
header,
|
header,
|
||||||
@@ -135,29 +138,37 @@ export async function handleConfigCommand(
|
|||||||
console.log(deps.header('CCS Config Dashboard'));
|
console.log(deps.header('CCS Config Dashboard'));
|
||||||
console.log('');
|
console.log('');
|
||||||
|
|
||||||
// Ensure CLIProxy service is running for dashboard features
|
const lifecyclePort = resolveLifecyclePort();
|
||||||
console.log(deps.info('Starting CLIProxy service...'));
|
if (deps.isRunningUnderSupervisord?.() ?? false) {
|
||||||
const cliproxyResult = await deps.ensureCliproxyService(resolveLifecyclePort(), verbose);
|
logger.info('cliproxy.supervisord_managed', 'Skipping direct CLIProxy startup in Docker', {
|
||||||
logger.info('cliproxy.ensure_result', 'Config command checked CLIProxy availability', {
|
port: lifecyclePort,
|
||||||
started: cliproxyResult.started,
|
});
|
||||||
alreadyRunning: cliproxyResult.alreadyRunning,
|
console.log(deps.info(`CLIProxy is managed by supervisord on port ${lifecyclePort}`));
|
||||||
configRegenerated: cliproxyResult.configRegenerated,
|
} else {
|
||||||
port: cliproxyResult.port || null,
|
// Ensure CLIProxy service is running for dashboard features
|
||||||
error: cliproxyResult.error || null,
|
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.started) {
|
||||||
if (cliproxyResult.alreadyRunning) {
|
if (cliproxyResult.alreadyRunning) {
|
||||||
console.log(deps.ok(`CLIProxy already running on port ${cliproxyResult.port}`));
|
console.log(deps.ok(`CLIProxy already running on port ${cliproxyResult.port}`));
|
||||||
if (cliproxyResult.configRegenerated) {
|
if (cliproxyResult.configRegenerated) {
|
||||||
console.log(deps.warn('Config updated - restart CLIProxy to apply changes'));
|
console.log(deps.warn('Config updated - restart CLIProxy to apply changes'));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(deps.ok(`CLIProxy started on port ${cliproxyResult.port}`));
|
||||||
}
|
}
|
||||||
} else {
|
} 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('');
|
console.log('');
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user