mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-12 22:14:22 +00:00
fix: stop untracked CLIProxy installs safely
This commit is contained in:
1 parent
a4b626aede
commit
ad01196964
3 files changed
+115
-15
No files matched your search
@@ -9,7 +9,7 @@ import { info, warn } from '../utils/ui';
|
|||||||
import { getBinDir, CLIPROXY_DEFAULT_PORT } from './config-generator';
|
import { getBinDir, CLIPROXY_DEFAULT_PORT } from './config-generator';
|
||||||
import { BinaryInfo, BinaryManagerConfig } from './types';
|
import { BinaryInfo, BinaryManagerConfig } from './types';
|
||||||
import { BACKEND_CONFIG, DEFAULT_BACKEND, CLIPROXY_MAX_STABLE_VERSION } from './platform-detector';
|
import { BACKEND_CONFIG, DEFAULT_BACKEND, CLIPROXY_MAX_STABLE_VERSION } from './platform-detector';
|
||||||
import { isProxyRunning, stopProxy } from './services/proxy-lifecycle-service';
|
import { stopProxy } from './services/proxy-lifecycle-service';
|
||||||
import { waitForPortFree } from '../utils/port-utils';
|
import { waitForPortFree } from '../utils/port-utils';
|
||||||
import { loadOrCreateUnifiedConfig } from '../config/unified-config-loader';
|
import { loadOrCreateUnifiedConfig } from '../config/unified-config-loader';
|
||||||
import {
|
import {
|
||||||
@@ -167,19 +167,18 @@ export async function installCliproxyVersion(
|
|||||||
const effectiveBackend = backend ?? getConfiguredBackend();
|
const effectiveBackend = backend ?? getConfiguredBackend();
|
||||||
const manager = new BinaryManager({ version, verbose, forceVersion: true }, effectiveBackend);
|
const manager = new BinaryManager({ version, verbose, forceVersion: true }, effectiveBackend);
|
||||||
|
|
||||||
// Check if proxy is running and stop it first
|
// Always attempt a best-effort stop first so we also catch untracked proxies
|
||||||
if (isProxyRunning()) {
|
// that are running without a session lock.
|
||||||
if (verbose) console.log(info('Stopping running CLIProxy before update...'));
|
if (verbose) console.log(info('Stopping running CLIProxy before update...'));
|
||||||
const result = await stopProxy();
|
const result = await stopProxy();
|
||||||
if (result.stopped) {
|
if (result.stopped) {
|
||||||
// Wait for port to be fully released
|
// Wait for port to be fully released
|
||||||
const portFree = await waitForPortFree(CLIPROXY_DEFAULT_PORT, 5000);
|
const portFree = await waitForPortFree(CLIPROXY_DEFAULT_PORT, 5000);
|
||||||
if (!portFree && verbose) {
|
if (!portFree && verbose) {
|
||||||
console.log(warn('Port did not free up in time, proceeding anyway...'));
|
console.log(warn('Port did not free up in time, proceeding anyway...'));
|
||||||
}
|
|
||||||
} else if (verbose && result.error) {
|
|
||||||
console.log(warn(`Could not stop proxy: ${result.error}`));
|
|
||||||
}
|
}
|
||||||
|
} else if (verbose && result.error && result.error !== 'No active CLIProxy session found') {
|
||||||
|
console.log(warn(`Could not stop proxy: ${result.error}`));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (manager.isBinaryInstalled()) {
|
if (manager.isBinaryInstalled()) {
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ export async function installDashboardCliproxyVersion(
|
|||||||
const backendLabel = backend === 'plus' ? 'CLIProxy Plus' : 'CLIProxy';
|
const backendLabel = backend === 'plus' ? 'CLIProxy Plus' : 'CLIProxy';
|
||||||
const shouldRestoreService = await wasProxyRunning(deps);
|
const shouldRestoreService = await wasProxyRunning(deps);
|
||||||
|
|
||||||
// The installer owns the stop-and-replace lifecycle: it stops a running proxy
|
// The installer owns the stop-and-replace lifecycle, including best-effort
|
||||||
// and waits for the port to free before swapping the binary.
|
// shutdown for tracked and untracked proxies before swapping the binary.
|
||||||
await deps.installCliproxyVersion(version, true, backend);
|
await deps.installCliproxyVersion(version, true, backend);
|
||||||
|
|
||||||
if (!shouldRestoreService) {
|
if (!shouldRestoreService) {
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
import { afterAll, beforeAll, describe, expect, it, mock } from 'bun:test';
|
||||||
|
|
||||||
|
const calls = {
|
||||||
|
stopProxy: 0,
|
||||||
|
waitForPortFree: 0,
|
||||||
|
deleteBinary: 0,
|
||||||
|
ensureBinary: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
mock.module('../../../src/utils/ui', () => ({
|
||||||
|
info: (message: string) => message,
|
||||||
|
warn: (message: string) => message,
|
||||||
|
}));
|
||||||
|
|
||||||
|
mock.module('../../../src/cliproxy/config-generator', () => ({
|
||||||
|
getBinDir: () => '/tmp/ccs-bin',
|
||||||
|
CLIPROXY_DEFAULT_PORT: 8317,
|
||||||
|
}));
|
||||||
|
|
||||||
|
mock.module('../../../src/cliproxy/platform-detector', () => ({
|
||||||
|
DEFAULT_BACKEND: 'plus',
|
||||||
|
CLIPROXY_MAX_STABLE_VERSION: '6.6.80',
|
||||||
|
BACKEND_CONFIG: {
|
||||||
|
plus: {
|
||||||
|
fallbackVersion: '6.6.80',
|
||||||
|
repo: 'router-for-me/CLIProxyAPIPlus',
|
||||||
|
},
|
||||||
|
original: {
|
||||||
|
fallbackVersion: '0.0.0',
|
||||||
|
repo: 'router-for-me/CLIProxyAPI',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
mock.module('../../../src/cliproxy/services/proxy-lifecycle-service', () => ({
|
||||||
|
stopProxy: async () => {
|
||||||
|
calls.stopProxy += 1;
|
||||||
|
return { stopped: false, error: 'No active CLIProxy session found' };
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
mock.module('../../../src/utils/port-utils', () => ({
|
||||||
|
waitForPortFree: async () => {
|
||||||
|
calls.waitForPortFree += 1;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
mock.module('../../../src/config/unified-config-loader', () => ({
|
||||||
|
loadOrCreateUnifiedConfig: () => ({
|
||||||
|
cliproxy: { backend: 'plus' },
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
mock.module('../../../src/cliproxy/binary', () => ({
|
||||||
|
checkForUpdates: async () => ({
|
||||||
|
hasUpdate: false,
|
||||||
|
currentVersion: '6.6.80',
|
||||||
|
latestVersion: '6.6.80',
|
||||||
|
fromCache: false,
|
||||||
|
checkedAt: Date.now(),
|
||||||
|
}),
|
||||||
|
deleteBinary: () => {
|
||||||
|
calls.deleteBinary += 1;
|
||||||
|
},
|
||||||
|
getBinaryPath: () => '/tmp/ccs-bin/plus/cliproxy',
|
||||||
|
isBinaryInstalled: () => false,
|
||||||
|
getBinaryInfo: async () => null,
|
||||||
|
getPinnedVersion: () => null,
|
||||||
|
savePinnedVersion: () => {},
|
||||||
|
clearPinnedVersion: () => {},
|
||||||
|
isVersionPinned: () => false,
|
||||||
|
getVersionPinPath: () => '/tmp/ccs-bin/plus/.version-pin',
|
||||||
|
readInstalledVersion: () => '6.6.80',
|
||||||
|
ensureBinary: async () => {
|
||||||
|
calls.ensureBinary += 1;
|
||||||
|
return '/tmp/ccs-bin/plus/cliproxy';
|
||||||
|
},
|
||||||
|
migrateVersionPin: () => {},
|
||||||
|
}));
|
||||||
|
|
||||||
|
let binaryManager: typeof import('../../../src/cliproxy/binary-manager');
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
binaryManager = await import('../../../src/cliproxy/binary-manager');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
mock.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('installCliproxyVersion', () => {
|
||||||
|
it('attempts to stop the proxy even when there is no tracked running session', async () => {
|
||||||
|
await binaryManager.installCliproxyVersion('6.7.1', false, 'plus');
|
||||||
|
|
||||||
|
expect(calls.stopProxy).toBe(1);
|
||||||
|
expect(calls.waitForPortFree).toBe(0);
|
||||||
|
expect(calls.deleteBinary).toBe(0);
|
||||||
|
expect(calls.ensureBinary).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in new issue
Block a user