import { spawn } from "child_process"; import path from "path"; import getPort from "get-port"; import { type ProxyConfig, saveProxyConfig, getProxyConfig, deleteProxyConfig, isProcessRunning, generateProxyId, listProxyConfigs, } from "./proxy-storage"; /** * Start a proxy in a separate process * @param upstreamUrl The upstream proxy URL * @param options Optional configuration * @returns Promise resolving to the proxy configuration */ export async function startProxyProcess( upstreamUrl: string, options: { port?: number; ignoreProxyCertificate?: boolean } = {} ): Promise { // Generate a unique ID for this proxy const id = generateProxyId(); // Get a random available port if not specified const port = options.port || (await getPort()); // Create the proxy configuration const config: ProxyConfig = { id, upstreamUrl, localPort: port, ignoreProxyCertificate: options.ignoreProxyCertificate || false, }; // Save the configuration before starting the process saveProxyConfig(config); // Build the command arguments const args = ["proxy-worker", "start", "--id", id]; // Spawn the process const child = spawn( process.execPath, [path.join(__dirname, "index.js"), ...args], { detached: true, stdio: "ignore", } ); // Unref the child to allow the parent to exit independently child.unref(); // Store the process ID config.pid = child.pid; config.localUrl = `http://localhost:${port}`; // Update the configuration with the process ID saveProxyConfig(config); // Wait a bit to ensure the proxy has started await new Promise((resolve) => setTimeout(resolve, 500)); return config; } /** * Stop a proxy process * @param id The proxy ID to stop * @returns Promise resolving to true if stopped, false if not found */ export async function stopProxyProcess(id: string): Promise { const config = getProxyConfig(id); if (!config || !config.pid) { return false; } try { // Check if the process is running if (isProcessRunning(config.pid)) { // Send SIGTERM to the process process.kill(config.pid); // Wait a bit to ensure the process has terminated await new Promise((resolve) => setTimeout(resolve, 300)); } // Delete the configuration deleteProxyConfig(id); return true; } catch (error) { console.error(`Error stopping proxy ${id}:`, error); return false; } } /** * Stop all proxy processes * @returns Promise resolving when all proxies are stopped */ export async function stopAllProxyProcesses(): Promise { const configs = listProxyConfigs(); for (const config of configs) { await stopProxyProcess(config.id); } }