Files
hytale-f2p-mirror/backend/utils/proxyClient.js
AMIAY b1d01a2f34 fix: Prevent JAR file corruption during proxy downloads
Fixed binary file corruption when downloading through proxy by using PassThrough stream to preserve data integrity while tracking download progress.
2026-02-01 17:35:25 +01:00

103 lines
2.8 KiB
JavaScript

const crypto = require('crypto');
const axios = require('axios');
const https = require('https');
const { PassThrough } = require('stream');
const PROXY_URL = process.env.HF2P_PROXY_URL || 'your_proxy_url_here';
const SECRET_KEY = process.env.HF2P_SECRET_KEY || 'your_secret_key_here_for_jwt';
function generateToken() {
const timestamp = Date.now().toString();
const hash = crypto
.createHmac('sha256', SECRET_KEY)
.update(timestamp)
.digest('hex');
return `${timestamp}:${hash}`;
}
async function proxyRequest(url, options = {}) {
const token = generateToken();
const urlObj = new URL(url);
const targetUrl = `${urlObj.protocol}//${urlObj.host}`;
const config = {
method: options.method || 'GET',
url: `${PROXY_URL}/proxy${urlObj.pathname}${urlObj.search}`,
headers: {
'X-Auth-Token': token,
'X-Target-URL': targetUrl,
...(options.headers || {})
},
timeout: options.timeout || 30000,
responseType: options.responseType
};
return axios(config);
}
function getProxyDownloadStream(url, onData) {
return new Promise((resolve, reject) => {
const token = generateToken();
const urlObj = new URL(url);
const targetUrl = `${urlObj.protocol}//${urlObj.host}`;
const proxyUrl = new URL(PROXY_URL);
const requestPath = `/proxy${urlObj.pathname}${urlObj.search}`;
const options = {
hostname: proxyUrl.hostname,
port: proxyUrl.port || (proxyUrl.protocol === 'https:' ? 443 : 80),
path: requestPath,
method: 'GET',
headers: {
'X-Auth-Token': token,
'X-Target-URL': targetUrl
}
};
const protocol = proxyUrl.protocol === 'https:' ? https : require('http');
const handleResponse = (response) => {
if (response.statusCode === 302 || response.statusCode === 301) {
const redirectUrl = response.headers.location;
if (redirectUrl.startsWith('http')) {
getProxyDownloadStream(redirectUrl, onData).then(resolve).catch(reject);
} else {
reject(new Error(`Invalid redirect: ${redirectUrl}`));
}
return;
}
if (response.statusCode !== 200) {
reject(new Error(`HTTP ${response.statusCode}`));
return;
}
if (onData) {
const totalSize = parseInt(response.headers['content-length'], 10);
let downloaded = 0;
const passThrough = new PassThrough();
response.on('data', (chunk) => {
downloaded += chunk.length;
onData(chunk, downloaded, totalSize);
});
response.pipe(passThrough);
resolve(passThrough);
} else {
resolve(response);
}
};
protocol.get(options, handleResponse).on('error', reject);
});
}
module.exports = {
proxyRequest,
getProxyDownloadStream,
generateToken
};